C++26 Core Language: Kleine Verbesserungen​
Es gibt weitere kleine Verbesserungen in C++26, darunter Pack-Indizierung, GrĂĽnde fĂĽr delete und Erweiterung von static assert.

(Bild: SerbioVas/Shutterstock)
- Rainer Grimm
Für C++26 sind neben den großen Ergänzungen Reflection und Contracts, die ich in einigen Beiträgen vorgestellt habe, auch ein paar kleinere, nützliche Ergänzungen vorgesehen.
Letzte Woche habe ich bereits Platzhaltern und erweiterten Zeichensatz gezeigt, und dieses Mal geht es um zusätzliche Ergänzungen für den C++-Standard.
static_assert
Erweiterung
Zunächst einmal ist hier die Syntax von static_assert
in C++11:
static_assert(compile time predicate, unevaluated string)
In C++26 kann der String ein benutzerdefinierter Datentyp mit den folgenden Eigenschaften sein:
- Has a
size()
method that produces an integer - Has a
data()
method that produces a pointer of character type such that - The elements in the
range [data(), data()+size())
are valid. (p2741r3)
static_assert
kann jetzt mit einem Formatierungsstring verwendet werden. Hier ist ein schönes Beispiel aus dem Proposal p2741r3. Ich habe daraus ein komplettes Programm gemacht.
// static_assert26.cpp
#include <iostream>
#include <format>
template <typename T, auto Expected, unsigned long Size = sizeof(T)>
constexpr bool ensure_size() {
static_assert(sizeof(T) == Expected, "Unexpected sizeof");
return true;
}
struct S {
int _{};
};
int main() {
std::cout << std::boolalpha << "C++11\n";
static_assert(ensure_size<S, 1>());
std::cout << std::boolalpha << "C++26\n";
static_assert(sizeof(S) == 1,
std::format("Unexpected sizeof: expected 1, got {}", sizeof(S)));
std::cout << '\n';
}
Das Template ensure_size
ist so definiert, dass es drei Parameter akzeptiert: einen Datentyp T, eine erwartete Größe Expected
und einen optionalen Parameter Size
, der standardmäßig der Größe von T
entspricht. Innerhalb der Funktion ĂĽberprĂĽft eine static_assert
-Anweisung, ob die Größe von T
gleich Expected
ist. Wenn die Größen nicht übereinstimmen, schlägt die Kompilierung mit der Meldung Unexpected sizeof
fehl. Die Funktion gibt true
zurĂĽck, wenn die Zusicherung erfĂĽllt ist.
Das Programm definiert dann eine einfache Struktur S, die ein einzelnes Integer-Element _
enthält. Diese Struktur wird verwendet, um die static_assert-
Funktionalität zu demonstrieren.
In der main
-Funktion gibt das Programm zunächst C++11 mit std::boolalpha
in der Konsole aus, um boolesche Werte als true
oder false
zu formatieren. AnschlieĂźend wird static_assert
mit aufgerufen, wodurch überprüft wird, ob die Größe von S
1 Byte beträgt. Da die Größe von S
tatsächlich größer als 1 Byte ist, schlägt diese Zusicherung fehl und führt zu einem Kompilierungsfehler.
Als Nächstes gibt das Programm C++26 in der Konsole aus und verwendet eine weitere static_assert
. Dieses Mal kommt std::format
zum Einsatz. Wenn die Größe von S
nicht 1 Byte, sondern 4 beträgt, schlägt die Kompilierung fehl.
Der Blick auf die GCC-Fehlermeldungen zeigt drei Fehler. std::format
ist bisher nicht constexpr
.
Pack-Indizierung
Die Pack-Indizierung ist wohl die Lieblings-Template-Verbesserung fĂĽr die Freunde der Template-Metaprogrammierung.
Das folgende Beispiel basiert auf dem Proposal P2662R3.
// packIndexing.cpp
#include <iostream>
#include <string>
template <typename... T>
constexpr auto first_plus_last(T... values) -> T...[0] {
return T...[0](values...[0] + values...[sizeof...(values)-1]);
}
int main() {
std::cout << '\n';
using namespace std::string_literals;
std::string hello = first_plus_last("Hello"s, "world"s, "goodbye"s, "World"s);
std::cout << "hello: " << hello << '\n';
constexpr int sum = first_plus_last(1, 2, 10);
std::cout << "sum: " << sum << '\n';
std::cout << '\n';
}
Das bereitgestellte Beispiel ist ein Funktions-Template, das die Summe des ersten und des letzten Elements eines Parameter-Packs berechnet.
Die Funktion ist als Template definiert, das eine variable Anzahl von Parametern eines beliebigen Datentyps T akzeptiert. Der RĂĽckgabetyp der Funktion wird mithilfe einer nachgestellten RĂĽckgabetyp-Syntax angegeben.
Der Funktionskörper gibt die Summe des ersten und letzten Elements des Parameter-Packs zurück. Der Ausdruck values...[0]
greift auf das erste Element zu und values...[sizeof...(values)-1]
greift auf das letzte Element zu.
Hier ist die Ausgabe des Programms:
delete
with reason
Mit C++26 kann man einen Grund fĂĽr ein delete
angeben. Ich gehe davon aus, dass sich das als Best Practice durchsetzen wird. Das folgende Programm soll das Vorgehen verdeutlichen.
// deleteReason.cpp
#include <iostream>
void func(double){}
template <typename T>
void func(T) = delete("Only for double");
int main(){
std::cout << '\n';
func(3.14);
func(3.14f);
std::cout << '\n';
}
Die Funktion func
ist auf zwei Arten überladen. Die erste Überladung ist eine reguläre Funktion, die ein double
als Parameter verwendet. Diese Funktion kann ohne Probleme mit einem double
-Argument aufgerufen werden.
Die zweite Ăśberladung ist ein Template, das jeden Datentyp als Parameter verwenden kann. Diese Funktion wird jedoch explizit mit dem = delete
-Spezifizierer mit der benutzerdefinierten Meldung „Only for double
“ gelöscht. Das bedeutet, dass jede Instanziierung mit einem anderen Datentyp als double
zu einem Kompilierungsfehler fĂĽhrt und die bereitgestellte Meldung angezeigt wird.
In der main
-Funktion ruft das Programm func
mit dem Argument 3.14
auf, das ein double
ist. Dieser Aufruf ist gĂĽltig und ruft die Nicht-Template-Ăśberladung von func
auf.
Als Nächstes versucht das Programm, func
mit dem Argument 3.14f
aufzurufen, das ein float
ist. Da es keine Nicht-Template-Ăśberladung von func
gibt, die einen float
akzeptiert, wĂĽrde die Template-Funktion instanziiert werden. Da die Template-Funktion jedoch fĂĽr alle Datentypen auĂźer double
gelöscht wurde, führt dieser Aufruf zu einem Kompilierungsfehler mit der Meldung „Only for double
“.
Wie geht es weiter?
In meinem nächsten Artikel werde ich auf die C++26-Bibliothek eingehen. (rme)