C++20 enthält die folgenden neuen Sprachfunktionen:
C++20 enthält die folgenden neuen Bibliotheksfunktionen:
C++17 enthält die folgenden neuen Sprachfunktionen:
C++17 enthält die folgenden neuen Bibliotheksfunktionen:
C++14 enthält die folgenden neuen Sprachfunktionen:
C++14 enthält die folgenden neuen Bibliotheksfunktionen:
C++11 enthält die folgenden neuen Sprachfunktionen:
C++11 enthält die folgenden neuen Bibliotheksfunktionen:
Hinweis: Während diese Beispiele die grundlegende Verwendung von Coroutinen veranschaulichen, passiert beim Kompilieren des Codes noch viel mehr. Diese Beispiele sollen keine vollständige Abdeckung der Coroutinen von C++20 darstellen. Da die
generator
undtask
-Klassen noch nicht von der Standardbibliothek bereitgestellt werden, habe ich zum Kompilieren dieser Beispiele die cppcoro-Bibliothek verwendet.
Coroutinen sind spezielle Funktionen, deren Ausführung unterbrochen und wieder aufgenommen werden kann. Um eine Coroutine zu definieren, müssen die Schlüsselwörter co_return
, co_await
oder co_yield
im Hauptteil der Funktion vorhanden sein. Die Coroutinen von C++20 sind stapellos. Sofern sie nicht vom Compiler optimiert werden, wird ihr Status auf dem Heap zugewiesen.
Ein Beispiel für eine Coroutine ist eine Generatorfunktion , die bei jedem Aufruf einen Wert liefert (d. h. generiert):
generator< int > range ( int start, int end) {
while (start < end) {
co_yield start;
start++;
}
// Implicit co_return at the end of this function:
// co_return;
}
for ( int n : range( 0 , 10 )) {
std::cout << n << std::endl;
}
Die obige range
generiert Werte beginnend bei start
bis end
(exklusiv), wobei jeder Iterationsschritt den aktuellen, in start
gespeicherten Wert ergibt. Der Generator behält seinen Zustand über jeden Aufruf von range
hinweg bei (in diesem Fall erfolgt der Aufruf für jede Iteration in der for-Schleife). co_yield
nimmt den gegebenen Ausdruck, gibt seinen Wert aus (dh gibt ihn zurück) und unterbricht die Coroutine an diesem Punkt. Bei der Wiederaufnahme wird die Ausführung nach dem co_yield
fortgesetzt.
Ein weiteres Beispiel für eine Coroutine ist eine Aufgabe , bei der es sich um eine asynchrone Berechnung handelt, die ausgeführt wird, wenn auf die Aufgabe gewartet wird:
task< void > echo (socket s) {
for (;;) {
auto data = co_await s. async_read ();
co_await async_write (s, data);
}
// Implicit co_return at the end of this function:
// co_return;
}
In diesem Beispiel wird das Schlüsselwort co_await
eingeführt. Dieses Schlüsselwort akzeptiert einen Ausdruck und unterbricht die Ausführung, wenn das, worauf Sie warten (in diesem Fall das Lesen oder Schreiben), nicht bereit ist. Andernfalls setzen Sie die Ausführung fort. (Beachten Sie, dass co_yield
unter der Haube co_await
verwendet.)
Verwenden einer Aufgabe zum trägen Auswerten eines Werts:
task< int > calculate_meaning_of_life () {
co_return 42 ;
}
auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
Konzepte sind benannte Prädikate zur Kompilierungszeit, die Typen einschränken. Sie haben folgende Form:
template < template-parameter-list >
concept concept-name = constraint-expression;
Dabei wird constraint-expression
zu einem booleschen Wert vom Typ constexpr ausgewertet. Einschränkungen sollten semantische Anforderungen modellieren, z. B. ob ein Typ numerisch oder hashbar ist. Ein Compilerfehler tritt auf, wenn ein bestimmter Typ das Konzept, an das er gebunden ist, nicht erfüllt (z. B. constraint-expression
gibt false
zurück). Da Einschränkungen zur Kompilierungszeit ausgewertet werden, können sie aussagekräftigere Fehlermeldungen und Laufzeitsicherheit bieten.
// `T` is not limited by any constraints.
template < typename T>
concept always_satisfied = true ;
// Limit `T` to integrals.
template < typename T>
concept integral = std::is_integral_v<T>;
// Limit `T` to both the `integral` constraint and signedness.
template < typename T>
concept signed_integral = integral<T> && std::is_signed_v<T>;
// Limit `T` to both the `integral` constraint and the negation of the `signed_integral` constraint.
template < typename T>
concept unsigned_integral = integral<T> && !signed_integral<T>;
Es gibt verschiedene syntaktische Formen zur Durchsetzung von Konzepten:
// Forms for function parameters:
// `T` is a constrained type template parameter.
template <my_concept T>
void f (T v);
// `T` is a constrained type template parameter.
template < typename T>
requires my_concept<T>
void f (T v);
// `T` is a constrained type template parameter.
template < typename T>
void f (T v) requires my_concept<T>;
// `v` is a constrained deduced parameter.
void f (my_concept auto v);
// `v` is a constrained non-type template parameter.
template <my_concept auto v>
void g ();
// Forms for auto-deduced variables:
// `foo` is a constrained auto-deduced value.
my_concept auto foo = ...;
// Forms for lambdas:
// `T` is a constrained type template parameter.
auto f = []<my_concept T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []< typename T> requires my_concept<T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []< typename T> (T v) requires my_concept<T> {
// ...
};
// `v` is a constrained deduced parameter.
auto f = [](my_concept auto v) {
// ...
};
// `v` is a constrained non-type template parameter.
auto g = []<my_concept auto v> () {
// ...
};
Das Schlüsselwort requires
wird entweder zum Starten einer requires
-Klausel oder eines requires
-Ausdrucks verwendet:
template < typename T>
requires my_concept<T> // `requires` clause.
void f (T);
template < typename T>
concept callable = requires (T f) { f (); }; // `requires` expression.
template < typename T>
requires requires (T x) { x + x; } // `requires` clause and expression on same line.
T add (T a, T b) {
return a + b;
}
Beachten Sie, dass die Parameterliste in einem requires
Ausdruck optional ist. Jede Anforderung in einem requires
ist eine der folgenden:
template < typename T>
concept callable = requires (T f) { f (); };
typename
gefolgt von einem Typnamen, bestätigen, dass der angegebene Typname gültig ist. struct foo {
int foo;
};
struct bar {
using value = int ;
value data;
};
struct baz {
using value = int ;
value data;
};
// Using SFINAE, enable if `T` is a `baz`.
template < typename T, typename = std:: enable_if_t <std::is_same_v<T, baz>>>
struct S {};
template < typename T>
using Ref = T&;
template < typename T>
concept C = requires {
// Requirements on type `T`:
typename T::value; // A) has an inner member named `value`
typename S<T>; // B) must have a valid class template specialization for `S`
typename Ref<T>; // C) must be a valid alias template substitution
};
template <C T>
void g (T a);
g (foo{}); // ERROR: Fails requirement A.
g (bar{}); // ERROR: Fails requirement B.
g (baz{}); // PASS.
template < typename T>
concept C = requires(T x) {
{*x} -> std::convertible_to< typename T::inner>; // the type of the expression `*x` is convertible to `T::inner`
{x + 1 } -> std::same_as< int >; // the expression `x + 1` satisfies `std::same_as<decltype((x + 1))>`
{x * 1 } -> std::convertible_to<T>; // the type of the expression `x * 1` is convertible to `T`
};
requires
, geben zusätzliche Einschränkungen an (z. B. solche für lokale Parameterargumente). template < typename T>
concept C = requires(T x) {
requires std::same_as< sizeof (x), size_t >;
};
Siehe auch: Konzeptbibliothek.
C++20 führt den Raumschiffoperator ( <=>
) als neue Möglichkeit zum Schreiben von Vergleichsfunktionen ein, die den Boilerplate reduzieren und Entwicklern helfen, eine klarere Vergleichssemantik zu definieren. Durch die Definition eines Drei-Wege-Vergleichsoperators werden die anderen Vergleichsoperatorfunktionen automatisch generiert (z. B. ==
, !=
, <
usw.).
Es werden drei Ordnungen eingeführt:
std::strong_ordering
: Die starke Reihenfolge unterscheidet zwischen Elementen, die gleich sind (identisch und austauschbar). Stellt die Reihenfolge less
“, greater
, equivalent
“ und equal
bereit. Beispiele für Vergleiche: Suche nach einem bestimmten Wert in einer Liste, Werte von Ganzzahlen, Groß-/Kleinschreibung beachtende Zeichenfolgen.std::weak_ordering
: Die schwache Reihenfolge unterscheidet zwischen Elementen, die gleichwertig sind (nicht identisch, können aber zu Vergleichszwecken austauschbar sein). Stellt die Reihenfolge less
“, greater
und equivalent
bereit. Beispiele für Vergleiche: Zeichenfolgen ohne Berücksichtigung der Groß-/Kleinschreibung, Sortieren, Vergleich einiger, aber nicht aller sichtbaren Mitglieder einer Klasse.std::partial_ordering
: Die partielle Ordnung folgt dem gleichen Prinzip der schwachen Ordnung, schließt jedoch den Fall ein, in dem eine Ordnung nicht möglich ist. Stellt die Reihenfolge less
“, greater
, equivalent
und unordered
bereit. Beispiele für Vergleiche: Gleitkommawerte (z. B. NaN
).Ein standardmäßiger Drei-Wege-Vergleichsoperator führt einen mitgliedweisen Vergleich durch:
struct foo {
int a;
bool b;
char c;
// Compare `a` first, then `b`, then `c` ...
auto operator <=>( const foo&) const = default ;
};
foo f1{ 0 , false , ' a ' }, f2{ 0 , true , ' b ' };
f1 < f2; // == true
f1 == f2; // == false
f1 >= f2; // == false
Sie können auch eigene Vergleiche definieren:
struct foo {
int x;
bool b;
char c;
std::strong_ordering operator <=>( const foo& other) const {
return x <=> other. x ;
}
};
foo f1{ 0 , false , ' a ' }, f2{ 0 , true , ' b ' };
f1 < f2; // == false
f1 == f2; // == true
f1 >= f2; // == true
Im C-Stil festgelegte Initialisierungssyntax. Alle Mitgliedsfelder, die nicht explizit in der angegebenen Initialisierungsliste aufgeführt sind, werden standardmäßig initialisiert.
struct A {
int x;
int y;
int z = 123 ;
};
A a {. x = 1 , . z = 2 }; // a.x == 1, a.y == 0, a.z == 2
Verwenden Sie in Lambda-Ausdrücken die vertraute Vorlagensyntax.
auto f = []< typename T>(std::vector<T> v) {
// ...
};
Diese Funktion vereinfacht gängige Codemuster, trägt dazu bei, die Gültigkeitsbereiche eng zu halten und bietet eine elegante Lösung für ein häufiges Lebensdauerproblem.
for ( auto v = std::vector{ 1 , 2 , 3 }; auto & e : v) {
std::cout << e;
}
// prints "123"
Gibt dem Optimierer einen Hinweis darauf, dass die gekennzeichnete Anweisung mit hoher Wahrscheinlichkeit ausgeführt wird.
switch (n) {
case 1 :
// ...
break ;
[[likely]] case 2 : // n == 2 is considered to be arbitrarily more
// ... // likely than any other value of n
break ;
}
Wenn eines der wahrscheinlichen/unwahrscheinlichen Attribute nach der rechten Klammer einer if-Anweisung erscheint, weist dies darauf hin, dass die Unteranweisung (Hauptteil) des Zweigs wahrscheinlich/unwahrscheinlich ausgeführt wird.
int random = get_random_number_between_x_and_y( 0 , 3 );
if (random > 0 ) [[likely]] {
// body of if statement
// ...
}
Es kann auch auf die Unteranweisung (Hauptteil) einer Iterationsanweisung angewendet werden.
while (unlikely_truthy_condition) [[unlikely]] {
// body of while statement
// ...
}
this
implizite Erfassung in einer Lambda-Erfassung mit [=]
ist jetzt veraltet. Bevorzugen Sie die explizite Erfassung mit [=, this]
oder [=, *this]
.
struct int_value {
int n = 0 ;
auto getter_fn () {
// BAD:
// return [=]() { return n; };
// GOOD:
return [=, * this ]() { return n; };
}
};
Klassen können jetzt in nicht typspezifischen Vorlagenparametern verwendet werden. Als Vorlagenargumente übergebene Objekte haben den Typ const T
, wobei T
der Typ des Objekts ist und über eine statische Speicherdauer verfügt.
struct foo {
foo () = default ;
constexpr foo ( int ) {}
};
template <foo f = {}>
auto get_foo () {
return f;
}
get_foo (); // uses implicit constructor
get_foo<foo{ 123 }>();
Virtuelle Funktionen können jetzt zur Kompilierungszeit constexpr
und ausgewertet werden. Virtuelle Funktionen constexpr
können virtuelle Nicht- constexpr
-Funktionen überschreiben und umgekehrt.
struct X1 {
virtual int f () const = 0;
};
struct X2 : public X1 {
constexpr virtual int f () const { return 2 ; }
};
struct X3 : public X2 {
virtual int f () const { return 3 ; }
};
struct X4 : public X3 {
constexpr virtual int f () const { return 4 ; }
};
constexpr X4 x4;
x4.f(); // == 4
Wählen Sie zur Kompilierungszeit bedingt aus, ob ein Konstruktor explizit gemacht wird oder nicht. explicit(true)
ist dasselbe wie die Angabe explicit
.
struct foo {
// Specify non-integral types (strings, floats, etc.) require explicit construction.
template < typename T>
explicit (!std::is_integral_v<T>) foo(T) {}
};
foo a = 123 ; // OK
foo b = " 123 " ; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)
foo c { " 123 " }; // OK
Ähnlich wie constexpr
-Funktionen, aber Funktionen mit einem consteval
-Spezifizierer müssen eine Konstante erzeugen. Diese werden immediate functions
genannt.
consteval int sqr ( int n) {
return n * n;
}
constexpr int r = sqr( 100 ); // OK
int x = 100 ;
int r2 = sqr(x); // ERROR: the value of 'x' is not usable in a constant expression
// OK if `sqr` were a `constexpr` function
Bringen Sie die Mitglieder einer Enumeration in den Gültigkeitsbereich, um die Lesbarkeit zu verbessern. Vor:
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string (rgba_color_channel channel) {
switch (channel) {
case rgba_color_channel::red: return " red " ;
case rgba_color_channel::green: return " green " ;
case rgba_color_channel::blue: return " blue " ;
case rgba_color_channel::alpha: return " alpha " ;
}
}
Nach:
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string (rgba_color_channel my_channel) {
switch (my_channel) {
using enum rgba_color_channel;
case red: return " red " ;
case green: return " green " ;
case blue: return " blue " ;
case alpha: return " alpha " ;
}
}
Erfassen Sie Parameterpakete nach Wert:
template < typename ... Args>
auto f (Args&&... args){
// BY VALUE:
return [... args = std::forward<Args>(args)] {
// ...
};
}
Parameterpakete nach Referenz erfassen:
template < typename ... Args>
auto f (Args&&... args){
// BY REFERENCE:
return [&... args = std::forward<Args>(args)] {
// ...
};
}
Stellt einen Standardtyp zur Darstellung von UTF-8-Zeichenfolgen bereit.
char8_t utf8_str[] = u8" u0123 " ;
Der constinit
Spezifizierer erfordert, dass eine Variable zur Kompilierungszeit initialisiert werden muss.
const char * g () { return " dynamic initialization " ; }
constexpr const char * f ( bool p) { return p ? " constant initializer " : g (); }
constinit const char * c = f( true ); // OK
constinit const char * d = g( false ); // ERROR: `g` is not constexpr, so `d` cannot be evaluated at compile-time.
Hilft bei der Unterstützung von Variadic-Makros, indem das angegebene Argument ausgewertet wird, wenn das Variadic-Makro nicht leer ist.
# define F (...) f( 0 __VA_OPT__ (,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F() // replaced by f(0)
Die Standardbibliothek stellt auch Konzepte für die Erstellung komplexerer Konzepte bereit. Einige davon umfassen:
Kernsprachkonzepte:
same_as
– gibt an, dass zwei Typen gleich sind.derived_from
– gibt an, dass ein Typ von einem anderen Typ abgeleitet ist.convertible_to
– gibt an, dass ein Typ implizit in einen anderen Typ konvertierbar ist.common_with
– gibt an, dass zwei Typen einen gemeinsamen Typ haben.integral
– gibt an, dass ein Typ ein integraler Typ ist.default_constructible
– gibt an, dass ein Objekt eines Typs standardmäßig erstellt werden kann.Vergleichskonzepte:
boolean
– gibt an, dass ein Typ in booleschen Kontexten verwendet werden kann.equality_comparable
– gibt an, dass operator==
eine Äquivalenzbeziehung ist.Objektkonzepte:
movable
– gibt an, dass ein Objekt eines Typs verschoben und ausgetauscht werden kann.copyable
– gibt an, dass ein Objekt eines Typs kopiert, verschoben und ausgetauscht werden kann.semiregular
– gibt an, dass ein Objekt eines Typs kopiert, verschoben, ausgetauscht und standardmäßig erstellt werden kann.regular
– gibt an, dass ein Typ regulär ist, also sowohl semiregular
als auch equality_comparable
ist.Abrufbare Konzepte:
invocable
– gibt an, dass ein aufrufbarer Typ mit einem bestimmten Satz von Argumenttypen aufgerufen werden kann.predicate
– gibt an, dass ein aufrufbarer Typ ein boolesches Prädikat ist.Siehe auch: Konzepte.
Kombinieren Sie die Einfachheit von printf
mit der Typsicherheit von iostream
. Verwendet geschweifte Klammern als Platzhalter und unterstützt benutzerdefinierte Formatierungen ähnlich den Spezifizierern im printf-Stil.
std::format ( " {1} {0} " , " world " , " hello " ); // == "hello world"
int x = 123 ;
std::string str = std::format( " x: {} " , x); // str == "x: 123"
// Format to an output iterator:
for ( auto x : { 1 , 2 , 3 }) {
std::format_to (std::ostream_iterator< char >{std::cout, " n " }, " {} " , x);
}
So formatieren Sie benutzerdefinierte Typen:
struct fraction {
int numerator;
int denominator;
};
template <>
struct std ::formatter<fraction>
{
constexpr auto parse (std::format_parse_context& ctx) {
return ctx. begin ();
}
auto format ( const fraction& f, std::format_context& ctx) const {
return std::format_to (ctx. out (), " {0:d}/{1:d} " , f. numerator , f. denominator );
}
};
fraction f{ 1 , 2 };
std::format ( " {} " , f); // == "1/2"
Puffert Ausgabevorgänge für den umschlossenen Ausgabestrom und sorgt so für Synchronisierung (dh keine Verschachtelung der Ausgabe).
std::osyncstream{std::cout} << " The value of x is: " << x << std::endl;
Ein Span ist eine Ansicht (dh eine nicht besitzende) Ansicht eines Containers, die grenzengeprüften Zugriff auf eine zusammenhängende Gruppe von Elementen bietet. Da Ansichten ihre Elemente nicht besitzen, sind sie kostengünstig zu erstellen und zu kopieren. Eine vereinfachte Art, über Ansichten nachzudenken, besteht darin, dass sie Verweise auf ihre Daten enthalten. Im Gegensatz zur Verwaltung eines Zeiger-/Iterator- und Längenfelds fasst ein Span beide in einem einzigen Objekt zusammen.
Spannen können eine dynamische Größe oder eine feste Größe haben (bekannt als ihre Ausdehnung ). Spannen mit fester Größe profitieren von der Grenzprüfung.
Span propagiert const nicht. Um einen schreibgeschützten Span zu erstellen, verwenden Sie std::span<const T>
.
Beispiel: Verwenden eines Bereichs mit dynamischer Größe zum Drucken von Ganzzahlen aus verschiedenen Containern.
void print_ints (std::span< const int > ints) {
for ( const auto n : ints) {
std::cout << n << std::endl;
}
}
print_ints (std::vector{ 1 , 2 , 3 });
print_ints (std::array< int , 5 >{ 1 , 2 , 3 , 4 , 5 });
int a[ 10 ] = { 0 };
print_ints (a);
// etc.
Beispiel: Eine Spanne mit statischer Größe kann für Container, die nicht mit der Ausdehnung der Spanne übereinstimmen, nicht kompiliert werden.
void print_three_ints (std::span< const int , 3 > ints) {
for ( const auto n : ints) {
std::cout << n << std::endl;
}
}
print_three_ints (std::vector{ 1 , 2 , 3 }); // ERROR
print_three_ints (std::array< int , 5 >{ 1 , 2 , 3 , 4 , 5 }); // ERROR
int a[ 10 ] = { 0 };
print_three_ints (a); // ERROR
std::array< int , 3 > b = { 1 , 2 , 3 };
print_three_ints (b); // OK
// You can construct a span manually if required:
std::vector c{ 1 , 2 , 3 };
print_three_ints (std::span< const int , 3 >{ c. data (), 3 }); // OK: set pointer and length field.
print_three_ints (std::span< const int , 3 >{ c. cbegin (), c. cend () }); // OK: use iterator pairs.
C++20 bietet einen neuen <bit>
-Header, der einige Bitoperationen einschließlich Popcount bereitstellt.
std::popcount ( 0u ); // 0
std::popcount ( 1u ); // 1
std::popcount ( 0b1111'0000u ); // 4
Mathematische Konstanten, einschließlich PI, Eulerzahl usw., definiert im <numbers>
-Header.
std::numbers:: pi ; // 3.14159...
std::numbers::e; // 2.71828...
Prädikatfunktion, die wahr ist, wenn sie in einem Kontext zur Kompilierungszeit aufgerufen wird.
constexpr bool is_compile_time () {
return std::is_constant_evaluated ();
}
constexpr bool a = is_compile_time(); // true
bool b = is_compile_time(); // false
auto p = std::make_shared< int []>( 5 ); // pointer to `int[5]`
// OR
auto p = std::make_shared< int [ 5 ]>(); // pointer to `int[5]`
Zeichenfolgen (und Zeichenfolgenansichten) verfügen jetzt über die Memberfunktionen starts_with
und ends_with
, um zu überprüfen, ob eine Zeichenfolge mit der angegebenen Zeichenfolge beginnt oder endet.
std::string str = " foobar " ;
str.starts_with( " foo " ); // true
str.ends_with( " baz " ); // false
Assoziative Container wie Mengen und Karten verfügen über eine contains
-Memberfunktion, die anstelle der Redewendung „Finden und Überprüfen des Endes des Iterators“ verwendet werden kann.
std::map< int , char > map {{ 1 , ' a ' }, { 2 , ' b ' }};
map.contains( 2 ); // true
map.contains( 123 ); // false
std::set< int > set { 1 , 2 , 3 };
set.contains( 2 ); // true
Eine sicherere Möglichkeit, ein Objekt von einem Typ in einen anderen umzuinterpretieren.
float f = 123.0 ;
int i = std::bit_cast< int >(f);
Berechnen Sie den Mittelpunkt zweier Ganzzahlen sicher (ohne Überlauf).
std::midpoint ( 1 , 3 ); // == 2
Konvertiert das angegebene Array/Array-ähnliche Objekt in ein std::array
.
std::to_array ( " foo " ); // returns `std::array<char, 4>`
std::to_array< int >({ 1 , 2 , 3 }); // returns `std::array<int, 3>`
int a[] = { 1 , 2 , 3 };
std::to_array (a); // returns `std::array<int, 3>`
Bindet die ersten N Argumente (wobei N die Anzahl der Argumente nach der angegebenen Funktion an std::bind_front
ist) an eine bestimmte freie Funktion, ein Lambda oder eine Mitgliedsfunktion.
const auto f = []( int a, int b, int c) { return a + b + c; };
const auto g = std::bind_front(f, 1 , 1 );
g ( 1 ); // == 3
Stellt std::erase
und/oder std::erase_if
für eine Vielzahl von STL-Containern wie String, Liste, Vektor, Karte usw. bereit.
Zum Löschen nach Wert verwenden Sie std::erase
, oder um ein Prädikat anzugeben, wann Elemente gelöscht werden sollen, verwenden Sie std::erase_if
. Beide Funktionen geben die Anzahl der gelöschten Elemente zurück.
std::vector v{ 0 , 1 , 0 , 2 , 0 , 3 };
std::erase (v, 0 ); // v == {1, 2, 3}
std::erase_if (v, []( int n) { return n == 0 ; }); // v == {1, 2, 3}
Hilfsfunktionen zur Benennung von Vergleichsergebnissen:
std::is_eq ( 0 <=> 0 ); // == true
std::is_lteq ( 0 <=> 1 ); // == true
std::is_gt ( 0 <=> 1 ); // == false
Siehe auch: Dreiervergleich.
Vergleicht lexikografisch zwei Bereiche mithilfe eines Drei-Wege-Vergleichs und liefert ein Ergebnis des stärksten anwendbaren Vergleichskategorietyps.
std::vector a{ 0 , 0 , 0 }, b{ 0 , 0 , 0 }, c{ 1 , 1 , 1 };
auto cmp_ab = std::lexicographical_compare_three_way(
a.begin(), a.end(), b.begin(), b.end());
std::is_eq (cmp_ab); // == true
auto cmp_ac = std::lexicographical_compare_three_way(
a.begin(), a.end(), c.begin(), c.end());
std::is_lt (cmp_ac); // == true
Siehe auch: Drei-Wege-Vergleich, Drei-Wege-Vergleichshilfen.
Automatische Ableitung von Vorlagenargumenten, ähnlich wie bei Funktionen, jetzt jedoch mit Klassenkonstruktoren.
template < typename T = float >
struct MyContainer {
T val;
MyContainer () : val{} {}
MyContainer (T val) : val{val} {}
// ...
};
MyContainer c1 { 1 }; // OK MyContainer<int>
MyContainer c2; // OK MyContainer<float>
Gemäß den Abzugsregeln von auto
und unter Berücksichtigung der Liste der zulässigen Typparameter der Nicht-Typ-Vorlage[*] können Vorlagenargumente aus den Typen ihrer Argumente abgeleitet werden:
template < auto ... seq>
struct my_integer_sequence {
// Implementation here ...
};
// Explicitly pass type `int` as template argument.
auto seq = std::integer_sequence< int , 0 , 1 , 2 >();
// Type is deduced to be `int`.
auto seq2 = my_integer_sequence< 0 , 1 , 2 >();
* – Beispielsweise können Sie kein double
als Vorlagenparametertyp verwenden, was dies ebenfalls zu einem ungültigen Abzug mit auto
macht.
Ein Faltausdruck führt eine Faltung eines Vorlagenparameterpakets über einen binären Operator durch.
(... op e)
oder (e op ...)
, wobei op
ein Fold-Operator und e
ein nicht erweitertes Parameterpaket ist, werden als unäre Folds bezeichnet.(e1 op ... op e2)
, wobei op
Faltoperatoren sind, wird als binäre Falte bezeichnet. Entweder e1
oder e2
ist ein nicht erweitertes Parameterpaket, aber nicht beides. template < typename ... Args>
bool logicalAnd (Args... args) {
// Binary folding.
return ( true && ... && args);
}
bool b = true ;
bool & b2 = b;
logicalAnd (b, b2, true ); // == true
template < typename ... Args>
auto sum (Args... args) {
// Unary folding.
return (... + args);
}
sum ( 1.0 , 2 . 0f , 3 ); // == 6.0
Änderungen am auto
Abzug bei Verwendung mit der einheitlichen Initialisierungssyntax. Zuvor auto x {3};
leitet einen std::initializer_list<int>
ab, der nun zu int
ableitet.
auto x1 { 1 , 2 , 3 }; // error: not a single element
auto x2 = { 1 , 2 , 3 }; // x2 is std::initializer_list<int>
auto x3 { 3 }; // x3 is int
auto x4 { 3.0 }; // x4 is double
Lambdas zur Kompilierungszeit mit constexpr
.
auto identity = []( int n) constexpr { return n; };
static_assert (identity( 123 ) == 123);
constexpr auto add = []( int x, int y) {
auto L = [=] { return x; };
auto R = [=] { return y; };
return [=] { return L () + R (); };
};
static_assert (add( 1 , 2 )() == 3);
constexpr int addOne ( int n) {
return [n] { return n + 1 ; }();
}
static_assert (addOne( 1 ) == 2);
this
nach Wert this
Erfassung in einer Lambda-Umgebung war bisher nur Referenzzweck. Ein Beispiel dafür, wo dies problematisch ist, ist asynchroner Code, der Rückrufe verwendet, die erfordern, dass ein Objekt verfügbar ist, möglicherweise über seine Lebensdauer hinaus. *this
(C++17) erstellt nun eine Kopie des aktuellen Objekts, während this
(C++11) weiterhin per Referenz erfasst.
struct MyObj {
int value { 123 };
auto getValueCopy () {
return [* this ] { return value; };
}
auto getValueRef () {
return [ this ] { return value; };
}
};
MyObj mo;
auto valueCopy = mo.getValueCopy();
auto valueRef = mo.getValueRef();
mo.value = 321 ;
valueCopy (); // 123
valueRef (); // 321
Der Inline-Spezifizierer kann sowohl auf Variablen als auch auf Funktionen angewendet werden. Eine inline deklarierte Variable hat die gleiche Semantik wie eine inline deklarierte Funktion.
// Disassembly example using compiler explorer.
struct S { int x; };
inline S x1 = S{ 321 }; // mov esi, dword ptr [x1]
// x1: .long 321
S x2 = S{ 123 }; // mov eax, dword ptr [.L_ZZ4mainE2x2]
// mov dword ptr [rbp - 8], eax
// .L_ZZ4mainE2x2: .long 123
Es kann auch zum Deklarieren und Definieren einer statischen Mitgliedsvariablen verwendet werden, sodass diese nicht in der Quelldatei initialisiert werden muss.
struct S {
S () : id{count++} {}
~S () { count--; }
int id;
static inline int count{ 0 }; // declare and initialize count to 0 within the class
};
Verwenden des Namespace-Auflösungsoperators zum Erstellen verschachtelter Namespace-Definitionen.
namespace A {
namespace B {
namespace C {
int i;
}
}
}
Der obige Code kann wie folgt geschrieben werden:
namespace A ::B::C {
int i;
}
Ein Vorschlag zur Destrukturierung der Initialisierung, der das Schreiben von auto [ x, y, z ] = expr;
wobei der Typ von expr
ein tupelartiges Objekt war, dessen Elemente an die Variablen x
, y
und z
(die dieses Konstrukt deklariert) gebunden wären. Tupelähnliche Objekte umfassen std::tuple
, std::pair
, std::array
und Aggregatstrukturen.
using Coordinate = std::pair< int , int >;
Coordinate origin () {
return Coordinate{ 0 , 0 };
}
const auto [ x, y ] = origin();
x; // == 0
y; // == 0
std::unordered_map<std::string, int > mapping {
{ " a " , 1 },
{ " b " , 2 },
{ " c " , 3 }
};
// Destructure by reference.
for ( const auto & [key, value] : mapping) {
// Do something with key and value
}
Neue Versionen der if
und switch
-Anweisungen, die gängige Codemuster vereinfachen und Benutzern dabei helfen, die Gültigkeitsbereiche eng zu halten.
{
std::lock_guard<std::mutex> lk (mx);
if (v. empty ()) v. push_back (val);
}
// vs.
if (std::lock_guard<std::mutex> lk (mx); v.empty()) {
v. push_back (val);
}
Foo gadget (args);
switch ( auto s = gadget.status()) {
case OK: gadget. zip (); break ;
case Bad: throw BadFoo (s. message ());
}
// vs.
switch (Foo gadget (args); auto s = gadget.status()) {
case OK: gadget. zip (); break ;
case Bad: throw BadFoo (s. message ());
}
Schreiben Sie Code, der abhängig von einer Bedingung zur Kompilierungszeit instanziiert wird.
template < typename T>
constexpr bool isIntegral () {
if constexpr (std::is_integral<T>::value) {
return true ;
} else {
return false ;
}
}
static_assert (isIntegral< int >() == true);
static_assert (isIntegral< char >() == true);
static_assert (isIntegral< double >() == false);
struct S {};
static_assert (isIntegral<S>() == false);
Ein Zeichenliteral, das mit u8
beginnt, ist ein Zeichenliteral vom Typ char
. Der Wert eines UTF-8-Zeichenliterals entspricht seinem ISO 10646-Codepunktwert.
char x = u8 ' x ' ;
Aufzählungen können jetzt mit geschweifter Syntax initialisiert werden.
enum byte : unsigned char {};
byte b { 0 }; // OK
byte c {- 1 }; // ERROR
byte d = byte{ 1 }; // OK
byte e = byte{ 256 }; // ERROR
C++17 führt drei neue Attribute ein: [[fallthrough]]
, [[nodiscard]]
und [[maybe_unused]]
.
[[fallthrough]]
zeigt dem Compiler an, dass das Durchfallen in einer Switch-Anweisung beabsichtigtes Verhalten ist. Dieses Attribut darf nur in einer Switch-Anweisung verwendet werden und muss vor der nächsten case/default-Beschriftung platziert werden. switch (n) {
case 1 :
// ...
[[fallthrough]];
case 2 :
// ...
break ;
case 3 :
// ...
[[fallthrough]];
default :
// ...
}
[[nodiscard]]
gibt eine Warnung aus, wenn eine Funktion oder Klasse dieses Attribut hat und sein Rückgabewert verworfen wird. [[nodiscard]] bool do_something () {
return is_success; // true for success, false for failure
}
do_something (); // warning: ignoring return value of 'bool do_something()',
// declared with attribute 'nodiscard'
// Only issues a warning when `error_info` is returned by value.
struct [[nodiscard]] error_info {
// ...
};
error_info do_something () {
error_info ei;
// ...
return ei;
}
do_something (); // warning: ignoring returned value of type 'error_info',
// declared with attribute 'nodiscard'
[[maybe_unused]]
zeigt dem Compiler an, dass eine Variable oder ein Parameter möglicherweise nicht verwendet wird und beabsichtigt ist. void my_callback (std::string msg, [[maybe_unused]] bool error) {
// Don't care if `msg` is an error message, just log it.
log (msg);
}
Der Operator __has_include (operand)
kann in #if
und #elif
-Ausdrücken verwendet werden, um zu prüfen, ob eine Header- oder Quelldatei ( operand
) für die Einbindung verfügbar ist oder nicht.
Ein Anwendungsfall hierfür wäre die Verwendung zweier Bibliotheken, die auf die gleiche Weise funktionieren, und die Verwendung der Backup-/experimentellen Bibliothek, wenn die bevorzugte nicht auf dem System gefunden wird.
# ifdef __has_include
# if __has_include(<optional>)
# include < optional >
# define have_optional 1
# elif __has_include(<experimental/optional>)
# include < experimental/optional >
# define have_optional 1
# define experimental_optional
# else
# define have_optional 0
# endif
# endif
Es kann auch verwendet werden, um Header einzuschließen, die unter unterschiedlichen Namen oder an verschiedenen Orten auf verschiedenen Plattformen vorhanden sind, ohne zu wissen, auf welcher Plattform das Programm läuft. OpenGL-Header sind ein gutes Beispiel dafür, die sich unter macOS im Verzeichnis OpenGL
und unter anderen im Verzeichnis GL
befinden Plattformen.
# ifdef __has_include
# if __has_include(<OpenGL/gl.h>)
# include < OpenGL/gl.h >
# include < OpenGL/glu.h >
# elif __has_include(<GL/gl.h>)
# include < GL/gl.h >
# include < GL/glu.h >
# else
# error No suitable OpenGL headers found.
# endif
# endif
Mit der Ableitung von Klassenvorlagenargumenten (CTAD) kann der Compiler Vorlagenargumente aus Konstruktorargumenten ableiten.
std::vector v{ 1 , 2 , 3 }; // deduces std::vector<int>
std::mutex mtx;
auto lck = std::lock_guard{ mtx }; // deduces to std::lock_guard<std::mutex>
auto p = new std::pair{ 1.0 , 2.0 }; // deduces to std::pair<double, double>*
Für benutzerdefinierte Typen können Ableitungsleitfäden verwendet werden, um den Compiler bei Bedarf bei der Ableitung von Vorlagenargumenten zu unterstützen:
template < typename T>
struct container {
container (T t) {}
template < typename Iter>
container (Iter beg, Iter end);
};
// deduction guide
template < typename Iter>
container (Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
container a{ 7 }; // OK: deduces container<int>
std::vector< double > v{ 1.0 , 2.0 , 3.0 };
auto b = container{ v. begin (), v. end () }; // OK: deduces container<double>
container c{ 5 , 6 }; // ERROR: std::iterator_traits<int>::value_type is not a type
Die Klassenvorlage std::variant
stellt eine typsichere union
dar. Eine Instanz von std::variant
enthält zu einem bestimmten Zeitpunkt einen Wert eines ihrer alternativen Typen (es ist auch möglich, dass er wertlos ist).
std::variant< int , double > v{ 12 };
std::get< int >(v); // == 12
std::get< 0 >(v); // == 12
v = 12.0 ;
std::get< double >(v); // == 12.0
std::get< 1 >(v); // == 12.0
Die Klassenvorlage std::optional
verwaltet einen optionalen enthaltenen Wert, also einen Wert, der vorhanden sein kann oder nicht. Ein häufiger Anwendungsfall für optional ist der Rückgabewert einer Funktion, die möglicherweise fehlschlägt.
std::optional<std::string> create ( bool b) {
if (b) {
return " Godzilla " ;
} else {
return {};
}
}
create ( false ).value_or( " empty " ); // == "empty"
create ( true ).value(); // == "Godzilla"
// optional-returning factory functions are usable as conditions of while and if
if ( auto str = create( true )) {
// ...
}
Ein typsicherer Container für Einzelwerte jeglichen Typs.
std::any x { 5 };
x.has_value() // == true
std::any_cast< int >(x) // == 5
std::any_cast< int &>(x) = 10 ;
std::any_cast< int >(x) // == 10
Ein nicht besitzender Verweis auf eine Zeichenfolge. Nützlich, um eine Abstraktion über Strings bereitzustellen (z. B. zum Parsen).
// Regular strings.
std::string_view cppstr { " foo " };
// Wide strings.
std::wstring_view wcstr_v { L" baz " };
// Character arrays.
char array[ 3 ] = { ' b ' , ' a ' , ' r ' };
std::string_view array_v (array, std::size(array));
std::string str { " trim me " };
std::string_view v {str};
v.remove_prefix(std::min(v.find_first_not_of( " " ), v.size()));
str; // == " trim me"
v; // == "trim me"
Rufen Sie ein Callable
Objekt mit Parametern auf. Beispiele für aufrufbare Objekte sind std::function
oder lambdas; Objekte, die ähnlich wie eine reguläre Funktion aufgerufen werden können.
template < typename Callable>
class Proxy {
Callable c_;
public:
Proxy (Callable c) : c_{ std::move (c) } {}
template < typename ... Args>
decltype ( auto ) operator ()(Args&&... args) {
// ...
return std::invoke (c_, std::forward<Args>(args)...);
}
};
const auto add = []( int x, int y) { return x + y; };
Proxy p{ add };
p ( 1 , 2 ); // == 3
Rufen Sie ein Callable
Objekt mit einem Tupel von Argumenten auf.
auto add = []( int x, int y) {
return x + y;
};
std::apply (add, std::make_tuple( 1 , 2 )); // == 3
Die neue std::filesystem
Bibliothek bietet eine Standardmethode zum Bearbeiten von Dateien, Verzeichnissen und Pfaden in einem Dateisystem.
Hier wird eine große Datei in einen temporären Pfad kopiert, sofern Platz verfügbar ist:
const auto bigFilePath { " bigFileToCopy " };
if (std::filesystem::exists(bigFilePath)) {
const auto bigFileSize { std::filesystem::file_size (bigFilePath)};
std::filesystem::path tmpPath { " /tmp " };
if ( std::filesystem::space (tmpPath). available > bigFileSize) {
std::filesystem::create_directory (tmpPath. append ( " example " ));
std::filesystem::copy_file (bigFilePath, tmpPath. append ( " newFile " ));
}
}
Der neue Typ std::byte
bietet eine Standardmethode zur Darstellung von Daten als Byte. Der Vorteil der Verwendung von std::byte
gegenüber char
oder unsigned char
besteht darin, dass es sich nicht um einen Zeichentyp und auch nicht um einen arithmetischen Typ handelt. während die einzigen verfügbaren Operatorüberladungen bitweise Operationen sind.
std::byte a { 0 };
std::byte b { 0xFF };
int i = std::to_integer< int >(b); // 0xFF
std::byte c = a & b;
int j = std::to_integer< int >(c); // 0
Beachten Sie, dass std::byte
einfach eine Aufzählung ist und die Initialisierung von Aufzählungen in geschweiften Klammern dank der direkten Listeninitialisierung von Aufzählungen möglich wird.
Verschieben von Knoten und Zusammenführen von Containern ohne den Aufwand teurer Kopien, Verschiebungen oder Heap-Zuweisungen/-Freigaben.
Elemente von einer Karte auf eine andere verschieben:
std::map< int , string> src {{ 1 , " one " }, { 2 , " two " }, { 3 , " buckle my shoe " }};
std::map< int , string> dst {{ 3 , " three " }};
dst.insert(src.extract(src.find( 1 ))); // Cheap remove and insert of { 1, "one" } from `src` to `dst`.
dst.insert(src.extract( 2 )); // Cheap remove and insert of { 2, "two" } from `src` to `dst`.
// dst == { { 1, "one" }, { 2, "two" }, { 3, "three" } };
Einen kompletten Satz einfügen:
std::set< int > src { 1 , 3 , 5 };
std::set< int > dst { 2 , 4 , 5 };
dst.merge(src);
// src == { 5 }
// dst == { 1, 2, 3, 4, 5 }
Einfügen von Elementen, die den Container überleben:
auto elementFactory () {
std::set<...> s;
s. emplace (...);
return s. extract (s. begin ());
}
s2.insert(elementFactory());
Den Schlüssel eines Kartenelements ändern:
std::map< int , string> m {{ 1 , " one " }, { 2 , " two " }, { 3 , " three " }};
auto e = m.extract( 2 );
e.key() = 4 ;
m.insert(std::move(e));
// m == { { 1, "one" }, { 3, "three" }, { 4, "two" } }
Viele der STL-Algorithmen, wie zum Beispiel die Methoden copy
, find
und sort
, begannen, die parallelen Ausführungsrichtlinien zu unterstützen: seq
, par
und par_unseq
die sich in „sequentiell“, „parallel“ und „parallel unsequenced“ übersetzen lassen.
std::vector< int > longVector;
// Find element using parallel execution policy
auto result1 = std::find(std::execution::par, std::begin(longVector), std::end(longVector), 2 );
// Sort elements using sequential execution policy
auto result2 = std::sort(std::execution::seq, std::begin(longVector), std::end(longVector));
Sticht n Elemente in der angegebenen Reihenfolge ab (ohne Ersatz), wobei jedes Element die gleiche Chance hat, ausgewählt zu werden.
const std::string ALLOWED_CHARS = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 " ;
std::string guid;
// Sample 5 characters from ALLOWED_CHARS.
std::sample (ALLOWED_CHARS.begin(), ALLOWED_CHARS.end(), std::back_inserter(guid),
5, std::mt19937{ std::random_device{}() });
std::cout << guid; // e.g. G1fW2
Klemmen Sie den angegebenen Wert zwischen einer Unter- und einer Obergrenze.
std::clamp ( 42 , - 1 , 1 ); // == 1
std::clamp (- 42 , - 1 , 1 ); // == -1
std::clamp ( 0 , - 1 , 1 ); // == 0
// `std::clamp` also accepts a custom comparator:
std::clamp ( 0 , - 1 , 1 , std::less<>{}); // == 0
Falten Sie einen bestimmten Bereich von Elementen. Vom Konzept her ähnlich wie std::accumulate
, aber std::reduce
führt die Faltung parallel durch. Da die Faltung parallel erfolgt, muss eine binäre Operation, wenn Sie sie angeben, assoziativ und kommutativ sein. Eine bestimmte binäre Operation sollte außerdem kein Element ändern oder Iteratoren innerhalb des angegebenen Bereichs ungültig machen.
Die standardmäßige binäre Operation ist std::plus mit einem Anfangswert von 0.
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::reduce (std::cbegin(a), std::cend(a)); // == 6
// Using a custom binary op:
std::reduce (std::cbegin(a), std::cend(a), 1, std::multiplies<>{}); // == 6
Zusätzlich können Sie Transformationen für Reduzierer angeben:
std::transform_reduce (std::cbegin(a), std::cend(a), 0, std::plus<>{}, times_ten); // == 60
const std::array< int , 3 > b{ 1 , 2 , 3 };
const auto product_times_ten = []( const auto a, const auto b) { return a * b * 10 ; };
std::transform_reduce (std::cbegin(a), std::cend(a), std::cbegin(b), 0, std::plus<>{}, product_times_ten); // == 140
Unterstützung für Präfixsummen (sowohl inklusive als auch exklusive Scans) sowie Transformationen.
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}); // 1 3 6
std::exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}); // 0 1 3
const auto times_ten = []( const auto n) { return n * 10 ; };
std::transform_inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}, times_ten); // 10 30 60
std::transform_exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}, times_ten); // 0 10 30
Größter gemeinsamer Teiler (GCD) und kleinstes gemeinsames Vielfaches (LCM).
const int p = 9 ;
const int q = 3 ;
std::gcd (p, q); // == 3
std::lcm (p, q); // == 9
Hilfsfunktion, die die Negation des Ergebnisses der gegebenen Funktion zurückgibt.
const std::ostream_iterator< int > ostream_it{ std::cout, " " };
const auto is_even = []( const auto n) { return n % 2 == 0 ; };
std::vector< int > v{ 0 , 1 , 2 , 3 , 4 };
// Print all even numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, is_even); // 0 2 4
// Print all odd (not even) numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, std::not_fn(is_even)); // 1 3
Konvertieren Sie Integrale und Gleitkommazahlen in einen String oder umgekehrt. Konvertierungen sind nicht auslösend, weisen keine Zuordnungen vor und sind sicherer als die Äquivalente aus der C-Standardbibliothek.
Benutzer sind dafür verantwortlich, genügend Speicher für std::to_chars
zuzuweisen, andernfalls schlägt die Funktion fehl, indem das Fehlercodeobjekt in ihrem Rückgabewert festgelegt wird.
Mit diesen Funktionen können Sie optional eine Basis (standardmäßig Basis 10) oder einen Formatbezeichner für die Eingabe vom Typ Floating übergeben.
std::to_chars
gibt einen (nicht konstanten) char-Zeiger zurück, der eins über dem Ende der Zeichenfolge liegt, in die die Funktion im angegebenen Puffer geschrieben hat, sowie ein Fehlercodeobjekt.std::from_chars
gibt einen const char-Zeiger zurück, der bei Erfolg dem an die Funktion übergebenen Endzeiger entspricht, sowie ein Fehlercodeobjekt.Beide von diesen Funktionen zurückgegebenen Fehlercodeobjekte entsprechen bei Erfolg dem standardmäßig initialisierten Fehlercodeobjekt.
Wandeln Sie die Zahl 123
in einen std::string
:
const int n = 123 ;
// Can use any container, string, array, etc.
std::string str;
str.resize( 3 ); // hold enough storage for each digit of `n`
const auto [ ptr, ec ] = std::to_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << str << std::endl; } // 123
else { /* handle failure */ }
Konvertieren Sie von einem std::string
mit dem Wert "123"
in eine Ganzzahl:
const std::string str{ " 123 " };
int n;
const auto [ ptr, ec ] = std::from_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << n << std::endl; } // 123
else { /* handle failure */ }
Stellt die Hilfsfunktionen „abs“, „round“, „ceil“ und „floor“ für std::chrono::duration
und std::chrono::time_point
bereit.
using seconds = std::chrono::seconds;
std::chrono::milliseconds d{ 5500 };
std::chrono::abs (d); // == 5s
std::chrono::round<seconds>(d); // == 6s
std::chrono::ceil<seconds>(d); // == 6s
std::chrono::floor<seconds>(d); // == 5s
Binäre Literale bieten eine praktische Möglichkeit, eine Zahl zur Basis 2 darzustellen. Es ist möglich, Ziffern mit '
zu trennen.
0b110 // == 6
0b1111'1111 // == 255
C++14 erlaubt jetzt den auto
Typbezeichner in der Parameterliste und ermöglicht so polymorphe Lambdas.
auto identity = []( auto x) { return x; };
int three = identity( 3 ); // == 3
std::string foo = identity( " foo " ); // == "foo"
Dies ermöglicht die Erstellung von Lambda-Captures, die mit beliebigen Ausdrücken initialisiert werden. Der dem erfassten Wert gegebene Name muss nicht mit Variablen in den umschließenden Bereichen verknüpft sein und führt einen neuen Namen in den Lambda-Körper ein. Der Initialisierungsausdruck wird ausgewertet, wenn das Lambda erstellt wird (nicht, wenn es aufgerufen wird ).
int factory ( int i) { return i * 10 ; }
auto f = [x = factory( 2 )] { return x; }; // returns 20
auto generator = [x = 0 ] () mutable {
// this would not compile without 'mutable' as we are modifying x on each call
return x++;
};
auto a = generator(); // == 0
auto b = generator(); // == 1
auto c = generator(); // == 2
Da es jetzt möglich ist, Werte in ein Lambda zu verschieben (oder weiterzuleiten ), die zuvor nur durch Kopieren oder Verweisen erfasst werden konnten, können wir jetzt nur verschiebbare Typen in einem Lambda nach Wert erfassen. Beachten Sie, dass im folgenden Beispiel das p
in der Capture-Liste von task2
auf der linken Seite von =
eine neue Variable ist, die privat für den Lambda-Körper ist und nicht auf das Original verweist p
.
auto p = std::make_unique< int >( 1 );
auto task1 = [=] { *p = 5 ; }; // ERROR: std::unique_ptr cannot be copied
// vs.
auto task2 = [p = std::move(p)] { *p = 5 ; }; // OK: p is move-constructed into the closure object
// the original p is empty after task2 is created
Bei Verwendung dieser Referenz können Captures andere Namen haben als die referenzierte Variable.
auto x = 1 ;
auto f = [&r = x, x = x * 10 ] {
++r;
return r + x;
};
f (); // sets x to 2 and returns 12
Mithilfe eines Typs auto
Rückgabe in C++14 versucht der Compiler, den Typ für Sie abzuleiten. Mit Lambdas können Sie jetzt den Rückgabetyp mithilfe von auto
ableiten, was die Rückgabe einer abgeleiteten Referenz oder einer R-Wert-Referenz ermöglicht.
// Deduce return type as `int`.
auto f ( int i) {
return i;
}
template < typename T>
auto & f (T& t) {
return t;
}
// Returns a reference to a deduced type.
auto g = []( auto & x) -> auto & { return f (x); };
int y = 123 ;
int & z = g(y); // reference to `y`
Der Typbezeichner decltype(auto)
leitet auch einen Typ ab, wie auto
tut. Es leitet jedoch Rückgabetypen ab und behält dabei deren Referenzen und CV-Qualifizierer bei, während dies bei auto
nicht der Fall ist.
const int x = 0 ;
auto x1 = x; // int
decltype ( auto ) x2 = x; // const int
int y = 0 ;
int & y1 = y;
auto y2 = y1; // int
decltype ( auto ) y3 = y1; // int&
int && z = 0 ;
auto z1 = std::move(z); // int
decltype ( auto ) z2 = std::move(z); // int&&
// Note: Especially useful for generic code!
// Return type is `int`.
auto f ( const int & i) {
return i;
}
// Return type is `const int&`.
decltype ( auto ) g( const int & i) {
return i;
}
int x = 123 ;
static_assert (std::is_same< const int &, decltype(f(x))>::value == 0);
static_assert (std::is_same< int , decltype(f(x))>::value == 1);
static_assert (std::is_same< const int &, decltype(g(x))>::value == 1);
Siehe auch: decltype (C++11)
.
In C++11 konnten constexpr
Funktionskörper nur einen sehr begrenzten Satz an Syntaxen enthalten, einschließlich (aber nicht beschränkt auf): typedef
s, using
s und eine einzelne return
Anweisung. In C++14 wird der Satz zulässiger Syntaxen erheblich erweitert und umfasst nun auch die gebräuchlichsten Syntaxen wie if
-Anweisungen, mehrere return
s, Schleifen usw.
constexpr int factorial ( int n) {
if (n <= 1 ) {
return 1 ;
} else {
return n * factorial (n - 1 );
}
}
factorial ( 5 ); // == 120
C++14 ermöglicht die Vorlage von Variablen:
template < class T >
constexpr T pi = T( 3.1415926535897932385 );
template < class T >
constexpr T e = T( 2.7182818284590452353 );
C++14 führt das Attribut [[deprecated]]
ein, um anzuzeigen, dass eine Einheit (Funktion, Klasse usw.) nicht empfohlen wird und wahrscheinlich Kompilierungswarnungen hervorruft. Wenn ein Grund angegeben wird, wird dieser in die Warnungen aufgenommen.
[[deprecated]]
void old_method ();
[[deprecated( " Use new_method instead " )]]
void legacy_method ();
Neue benutzerdefinierte Literale für Standardbibliothekstypen, einschließlich neuer integrierter Literale für chrono
und basic_string
. Diese können constexpr
sein, was bedeutet, dass sie zur Kompilierungszeit verwendet werden können. Einige Verwendungsmöglichkeiten für diese Literale umfassen das Parsen von Ganzzahlen zur Kompilierungszeit, binäre Literale und imaginäre Zahlenliterale.
using namespace std ::chrono_literals ;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440
Die Klassenvorlage std::integer_sequence
stellt eine Folge von Ganzzahlen zur Kompilierungszeit dar. Darauf sind ein paar Helfer aufgebaut:
std::make_integer_sequence<T, N>
– erstellt eine Sequenz von 0, ..., N - 1
mit Typ T
.std::index_sequence_for<T...>
– konvertiert ein Vorlagenparameterpaket in eine Ganzzahlsequenz.Konvertieren Sie ein Array in ein Tupel:
template < typename Array, std:: size_t ... I>
decltype ( auto ) a2t_impl( const Array& a, std::integer_sequence<std:: size_t , I...>) {
return std::make_tuple (a[I]...);
}
template < typename T, std:: size_t N, typename Indices = std::make_index_sequence<N>>
decltype ( auto ) a2t( const std::array<T, N>& a) {
return a2t_impl (a, Indices ());
}
std::make_unique
ist aus folgenden Gründen die empfohlene Methode zum Erstellen von Instanzen von std::unique_ptr
s:
new
Operators.foo
wie folgt auf: foo (std::unique_ptr<T>{ new T{}}, function_that_throws(), std::unique_ptr<T>{ new T{}});
Dem Compiler steht es frei, new T{}
aufzurufen, dann function_that_throws()
usw. Da wir in der ersten Konstruktion eines T
Daten auf dem Heap zugewiesen haben, haben wir hier ein Leck eingeführt. Mit std::make_unique
erhalten wir Ausnahmesicherheit:
foo (std::make_unique<T>(), function_that_throws(), std::make_unique<T>());
Weitere Informationen zu std::unique_ptr
und std::shared_ptr
finden Sie im Abschnitt über intelligente Zeiger (C++11).
Das Verschieben eines Objekts bedeutet, dass das Eigentum an einer von ihm verwalteten Ressource auf ein anderes Objekt übertragen wird.
Der erste Vorteil der Bewegungssemantik ist die Leistungsoptimierung. Wenn ein Objekt das Ende seiner Lebensdauer erreicht, sei es, weil es temporär ist oder weil es explizit std::move
aufgerufen wird, ist eine Verschiebung oft eine günstigere Möglichkeit, Ressourcen zu übertragen. Wenn Sie beispielsweise einen std::vector
verschieben, werden lediglich einige Zeiger und der interne Status auf den neuen Vektor kopiert. Beim Kopieren müssten Sie jedes einzelne enthaltene Element im Vektor kopieren, was teuer und unnötig ist, wenn der alte Vektor bald verfügbar sein wird zerstört.
Durch Verschiebungen können auch nicht kopierbare Typen wie std::unique_ptr
s (intelligente Zeiger) auf Sprachebene garantieren, dass jeweils nur eine Instanz einer Ressource verwaltet wird, während gleichzeitig eine Instanz übertragen werden kann zwischen Bereichen.
Weitere Informationen finden Sie in den Abschnitten zu: R-Wert-Referenzen, speziellen Mitgliedsfunktionen für die Verschiebungssemantik, std::move
, std::forward
und forwarding references
.
C++11 führt eine neue Referenz ein, die als R-Wert-Referenz bezeichnet wird. Mit der Syntax T&&
wird ein R-Wert-Verweis auf T
erstellt, bei dem es sich um einen Nicht-Vorlagentypparameter (z. B. int
oder einen benutzerdefinierten Typ) handelt. R-Wert-Referenzen binden nur an R-Werte.
Typabzug mit l-Werten und r-Werten:
int x = 0 ; // `x` is an lvalue of type `int`
int & xl = x; // `xl` is an lvalue of type `int&`
int && xr = x; // compiler error -- `x` is an lvalue
int && xr2 = 0 ; // `xr2` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
void f ( int & x) {}
void f ( int && x) {}
f (x); // calls f(int&)
f (xl); // calls f(int&)
f ( 3 ); // calls f(int&&)
f (std::move(x)); // calls f(int&&)
f (xr2); // calls f(int&)
f (std::move(xr2)); // calls f(int&& x)
Siehe auch: std::move
, std::forward
, forwarding references
.
Auch (inoffiziell) als Universalreferenzen bekannt. Eine Weiterleitungsreferenz wird mit der Syntax T&&
erstellt, wobei T
ein Vorlagentypparameter ist, oder mit auto&&
. Dies ermöglicht eine perfekte Weiterleitung : die Möglichkeit, Argumente unter Beibehaltung ihrer Wertkategorie zu übergeben (z. B. bleiben L-Werte als L-Werte, temporäre Werte werden als R-Werte weitergeleitet).
Durch Weiterleitungsreferenzen kann eine Referenz je nach Typ entweder an einen L-Wert oder einen R-Wert gebunden werden. Weiterleitungsreferenzen folgen den Regeln für das Reduzieren von Referenzen :
T& &
wird T&
T& &&
wird T&
T&& &
wird T&
T&& &&
wird zu T&&
auto
Typabzug mit L-Werten und R-Werten:
int x = 0 ; // `x` is an lvalue of type `int`
auto && al = x; // `al` is an lvalue of type `int&` -- binds to the lvalue, `x`
auto && ar = 0 ; // `ar` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
Parameterableitung vom Vorlagentyp mit L-Werten und R-Werten:
// Since C++14 or later:
void f ( auto && t) {
// ...
}
// Since C++11 or later:
template < typename T>
void f (T&& t) {
// ...
}
int x = 0 ;
f ( 0 ); // T is int, deduces as f(int &&) => f(int&&)
f (x); // T is int&, deduces as f(int& &&) => f(int&)
int & y = x;
f (y); // T is int&, deduces as f(int& &&) => f(int&)
int && z = 0 ; // NOTE: `z` is an lvalue with type `int&&`.
f (z); // T is int&, deduces as f(int& &&) => f(int&)
f (std::move(z)); // T is int, deduces as f(int &&) => f(int&&)
Siehe auch: std::move
, std::forward
, rvalue references
.
Die ...
Syntax erstellt ein Parameterpaket oder erweitert eines. Ein Vorlagenparameterpaket ist ein Vorlagenparameter, der null oder mehr Vorlagenargumente (Nichttypen, Typen oder Vorlagen) akzeptiert. Eine Vorlage mit mindestens einem Parameterpaket wird als variadische Vorlage bezeichnet.
template < typename ... T>
struct arity {
constexpr static int value = sizeof ...(T);
};
static_assert (arity<>::value == 0 );
static_assert (arity< char , short , int >::value == 3 );
Eine interessante Verwendung hierfür ist das Erstellen einer Initialisierungsliste aus einem Parameterpaket , um verschiedene Funktionsargumente zu iterieren.
template < typename First, typename ... Args>
auto sum ( const First first, const Args... args) -> decltype(first) {
const auto values = {first, args...};
return std::accumulate (values. begin (), values. end (), First{ 0 });
}
sum ( 1 , 2 , 3 , 4 , 5 ); // 15
sum ( 1 , 2 , 3 ); // 6
sum ( 1.5 , 2.0 , 3.7 ); // 7.2
Ein leichter, arrayartiger Container mit Elementen, der mit der Syntax einer „geschweiften Liste“ erstellt wurde. Beispielsweise erstellt { 1, 2, 3 }
eine Folge von Ganzzahlen vom Typ std::initializer_list<int>
. Nützlich als Ersatz für die Übergabe eines Vektors von Objekten an eine Funktion.
int sum ( const std::initializer_list< int >& list) {
int total = 0 ;
for ( auto & e : list) {
total += e;
}
return total;
}
auto list = { 1 , 2 , 3 };
sum (list); // == 6
sum ({ 1 , 2 , 3 }); // == 6
sum ({}); // == 0
Behauptungen, die zur Kompilierungszeit ausgewertet werden.
constexpr int x = 0 ;
constexpr int y = 1 ;
static_assert (x == y, " x != y " );
auto
typisierte Variablen werden vom Compiler entsprechend dem Typ ihres Initialisierers abgeleitet.
auto a = 3.14 ; // double
auto b = 1 ; // int
auto & c = b; // int&
auto d = { 0 }; // std::initializer_list<int>
auto && e = 1 ; // int&&
auto && f = b; // int&
auto g = new auto ( 123 ); // int*
const auto h = 1 ; // const int
auto i = 1 , j = 2 , k = 3 ; // int, int, int
auto l = 1 , m = true , n = 1.61 ; // error -- `l` deduced to be int, `m` is bool
auto o; // error -- `o` requires initializer
Äußerst nützlich für die Lesbarkeit, insbesondere bei komplizierten Typen:
std::vector< int > v = ...;
std::vector< int >::const_iterator cit = v.cbegin();
// vs.
auto cit = v.cbegin();
Funktionen können den Rückgabetyp auch mithilfe von auto
ableiten. In C++11 muss ein Rückgabetyp entweder explizit oder mithilfe von decltype
wie folgt angegeben werden:
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2 ); // == 3
add ( 1 , 2.0 ); // == 3.0
add ( 1.5 , 1.5 ); // == 3.0
Der abschließende Rückgabetyp im obigen Beispiel ist der deklarierte Typ (siehe Abschnitt über decltype
) des Ausdrucks x + y
. Wenn beispielsweise x
eine Ganzzahl und y
ein Double ist, ist decltype(x + y)
ein Double. Daher leitet die obige Funktion den Typ abhängig davon ab, welchen Typ der Ausdruck x + y
ergibt. Beachten Sie, dass der nachfolgende Rückgabetyp Zugriff auf seine Parameter hat, und this
bei Bedarf.
Ein lambda
ist ein unbenanntes Funktionsobjekt, das Variablen im Gültigkeitsbereich erfassen kann. Es verfügt über: eine Erfassungsliste ; ein optionaler Parametersatz mit einem optionalen abschließenden Rückgabetyp; und ein Körper. Beispiele für Capture-Listen:
[]
– erfasst nichts.[=]
– lokale Objekte (lokale Variablen, Parameter) im Bereich nach Wert erfassen.[&]
– Erfassen Sie lokale Objekte (lokale Variablen, Parameter) im Gültigkeitsbereich per Referenz.[this]
– Erfassen Sie this
als Referenz.[a, &b]
– Objekte a
nach Wert, b
nach Referenz erfassen. int x = 1 ;
auto getX = [=] { return x; };
getX (); // == 1
auto addX = [=]( int y) { return x + y; };
addX ( 1 ); // == 2
auto getXRef = [&]() -> int & { return x; };
getXRef (); // int& to `x`
Standardmäßig können Werterfassungen nicht innerhalb des Lambda geändert werden, da die vom Compiler generierte Methode als const
markiert ist. Das Schlüsselwort mutable
ermöglicht das Ändern erfasster Variablen. Das Schlüsselwort wird nach der Parameterliste platziert (die vorhanden sein muss, auch wenn sie leer ist).
int x = 1 ;
auto f1 = [&x] { x = 2 ; }; // OK: x is a reference and modifies the original
auto f2 = [x] { x = 2 ; }; // ERROR: the lambda can only perform const-operations on the captured value
// vs.
auto f3 = [x]() mutable { x = 2 ; }; // OK: the lambda can perform any operations on the captured value
decltype
ist ein Operator, der den deklarierten Typ eines an ihn übergebenen Ausdrucks zurückgibt. Lebenslauf-Qualifikationsmerkmale und Referenzen werden beibehalten, wenn sie Teil des Ausdrucks sind. Beispiele für decltype
:
int a = 1 ; // `a` is declared as type `int`
decltype (a) b = a; // `decltype(a)` is `int`
const int & c = a; // `c` is declared as type `const int&`
decltype (c) d = a; // `decltype(c)` is `const int&`
decltype ( 123 ) e = 123; // `decltype(123)` is `int`
int && f = 1 ; // `f` is declared as type `int&&`
decltype (f) g = 1; // `decltype(f) is `int&&`
decltype ((a)) h = g; // `decltype((a))` is int&
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2.0 ); // `decltype(x + y)` => `decltype(3.0)` => `double`
Siehe auch: decltype(auto) (C++14)
.
Semantisch ähnlich wie die Verwendung einer typedef
, Typaliase mit using
sind jedoch einfacher zu lesen und mit Vorlagen kompatibel.
template < typename T>
using Vec = std::vector<T>;
Vec< int > v; // std::vector<int>
using String = std::string;
String s { " foo " };
C++11 führt einen neuen Nullzeigertyp ein, der NULL
Makro von C ersetzen soll. nullptr
selbst ist vom Typ std::nullptr_t
und kann implizit in Zeigertypen konvertiert werden. Im Gegensatz zu NULL
ist es außer bool
nicht in ganzzahlige Typen konvertierbar.
void foo ( int );
void foo ( char *);
foo ( NULL ); // error -- ambiguous
foo ( nullptr ); // calls foo(char*)
Typsichere Aufzählungen, die eine Vielzahl von Problemen mit Aufzählungen im C-Stil lösen, darunter: implizite Konvertierungen, Unfähigkeit, den zugrunde liegenden Typ anzugeben, Bereichsverschmutzung.
// Specifying underlying type as `unsigned int`
enum class Color : unsigned int { Red = 0xff0000 , Green = 0xff00 , Blue = 0xff };
// `Red`/`Green` in `Alert` don't conflict with `Color`
enum class Alert : bool { Red, Green };
Color c = Color::Red;
Attribute bieten eine universelle Syntax über __attribute__(...)
, __declspec
usw.
// `noreturn` attribute indicates `f` doesn't return.
[[ noreturn ]] void f () {
throw " error " ;
}
Konstante Ausdrücke sind Ausdrücke, die möglicherweise vom Compiler zur Kompilierungszeit bewertet werden. In späteren Versionen können nur Nichtkomplexberechnungen in einem konstanten Ausdruck durchgeführt werden (diese Regeln werden zunehmend entspannt). Verwenden Sie den constexpr
-Spezifizierer, um die Variable, Funktion usw. anzuzeigen. Ist ein konstanter Ausdruck.
constexpr int square ( int x) {
return x * x;
}
int square2 ( int x) {
return x * x;
}
int a = square( 2 ); // mov DWORD PTR [rbp-4], 4
int b = square2( 2 ); // mov edi, 2
// call square2(int)
// mov DWORD PTR [rbp-8], eax
Beachten Sie im vorherigen Snippet, dass die Berechnung beim Rufen square
zur Kompilierungszeit durchgeführt wird und das Ergebnis dann in die Codeerzeugung eingebettet ist, während square2
zur Laufzeit aufgerufen wird.
constexpr
-Werte sind solche, die der Compiler bewerten kann, aber es wird nicht garantiert, dass er zum Zeitpunkt der Kompilierung garantiert wird:
const int x = 123 ;
constexpr const int & y = x; // error -- constexpr variable `y` must be initialized by a constant expression
Konstante Ausdrücke mit Klassen:
struct Complex {
constexpr Complex ( double r, double i) : re{r}, im{i} { }
constexpr double real () { return re; }
constexpr double imag () { return im; }
private:
double re;
double im;
};
constexpr Complex I ( 0 , 1 );
Konstruktoren können jetzt andere Konstruktoren in derselben Klasse mit einer Initialisiererliste aufrufen.
struct Foo {
int foo;
Foo ( int foo) : foo{foo} {}
Foo () : Foo( 0 ) {}
};
Foo foo;
foo.foo; // == 0
Mit benutzerdefinierten Literalen können Sie die Sprache erweitern und Ihre eigene Syntax hinzufügen. Um ein wörtliches Erstellen zu erstellen, definieren Sie einen T operator "" X(...) { ... }
der einen Typ T
mit einem Namen X
zurückgibt. Beachten Sie, dass der Name dieser Funktion den Namen des Literales definiert. Alle wörtlichen Namen, die nicht mit einem Unterstrich beginnen, sind reserviert und werden nicht aufgerufen. Es gibt Regeln für die Parameter, die eine benutzerdefinierte wörtliche Funktion akzeptieren sollte, je nachdem, welche Art der Wörtel aufgerufen wird.
Umwandlung von Celsius in Fahrenheit:
// `unsigned long long` parameter required for integer literal.
long long operator " " _celsius( unsigned long long tempCelsius) {
return std::llround (tempCelsius * 1.8 + 32 );
}
24_celsius; // == 75
String to Ganzzahlkonvertierung:
// `const char*` and `std::size_t` required as parameters.
int operator " " _int( const char * str, std:: size_t ) {
return std::stoi (str);
}
" 123 " _int; // == 123, with type `int`
Gibt an, dass eine virtuelle Funktion eine andere virtuelle Funktion überschreibt. Wenn die virtuelle Funktion die virtuelle Funktion eines Elternteils nicht überschreibt, wirft ein Compiler -Fehler aus.
struct A {
virtual void foo ();
void bar ();
};
struct B : A {
void foo () override ; // correct -- B::foo overrides A::foo
void bar () override ; // error -- A::bar is not virtual
void baz () override ; // error -- B::baz does not override A::baz
};
Gibt an, dass eine virtuelle Funktion in einer abgeleiteten Klasse nicht überschrieben werden kann oder dass eine Klasse nicht von geerbt werden kann.
struct A {
virtual void foo ();
};
struct B : A {
virtual void foo () final ;
};
struct C : B {
virtual void foo (); // error -- declaration of 'foo' overrides a 'final' function
};
Klasse kann nicht geerbt werden.
struct A final {};
struct B : A {}; // error -- base 'A' is marked 'final'
Eine elegantere und effizientere Möglichkeit, eine Standardimplementierung einer Funktion zu ermöglichen, z. B. eines Konstruktors.
struct A {
A () = default ;
A ( int x) : x{x} {}
int x { 1 };
};
A a; // a.x == 1
A a2 { 123 }; // a.x == 123
Mit Erbschaft:
struct B {
B () : x{ 1 } {}
int x;
};
struct C : B {
// Calls B::B
C () = default ;
};
C c; // c.x == 1
Eine elegantere und effizientere Möglichkeit, eine gelöschte Implementierung einer Funktion bereitzustellen. Nützlich, um Kopien auf Objekten zu verhindern.
class A {
int x;
public:
A ( int x) : x{x} {};
A ( const A&) = delete ;
A& operator =( const A&) = delete ;
};
A x { 123 };
A y = x; // error -- call to deleted copy constructor
y = x; // error -- operator= deleted
Syntaktischer Zucker zum Iterieren über die Elemente eines Behälters.
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int & x : a) x *= 2 ;
// a == { 2, 4, 6, 8, 10 }
Beachten Sie den Unterschied bei der Verwendung int
im Gegensatz zu int&
:
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int x : a) x *= 2 ;
// a == { 1, 2, 3, 4, 5 }
Der Kopierkonstruktor und der Kopierzuweisungsbetreiber werden aufgerufen, wenn Kopien erstellt werden, und mit C ++ 11 durchs Einführung der Bewegungssemantik gibt es nun einen Move Constructor- und Move -Zuweisungsoperator für Moves.
struct A {
std::string s;
A () : s{ " test " } {}
A ( const A& o) : s{o. s } {}
A (A&& o) : s{ std::move (o. s )} {}
A& operator =(A&& o) {
s = std::move (o. s );
return * this ;
}
};
A f (A a) {
return a;
}
A a1 = f(A{}); // move-constructed from rvalue temporary
A a2 = std::move(a1); // move-constructed using std::move
A a3 = A{};
a2 = std::move(a3); // move-assignment using std::move
a1 = f(A{}); // move-assignment from rvalue temporary
Konvertierende Konstruktoren konvertieren Werte der versperrten Listensyntax in Konstruktorargumente.
struct A {
A ( int ) {}
A ( int , int ) {}
A ( int , int , int ) {}
};
A a { 0 , 0 }; // calls A::A(int, int)
A b ( 0 , 0 ); // calls A::A(int, int)
A c = { 0 , 0 }; // calls A::A(int, int)
A d { 0 , 0 , 0 }; // calls A::A(int, int, int)
Beachten Sie, dass die verspülte List -Syntax keine Verengung erlaubt:
struct A {
A ( int ) {}
};
A a ( 1.1 ); // OK
A b { 1.1 }; // Error narrowing conversion from double to int
Beachten Sie, dass ein Konstruktor, wenn ein Konstruktor eine std::initializer_list
akzeptiert, stattdessen aufgerufen wird:
struct A {
A ( int ) {}
A ( int , int ) {}
A ( int , int , int ) {}
A (std::initializer_list< int >) {}
};
A a { 0 , 0 }; // calls A::A(std::initializer_list<int>)
A b ( 0 , 0 ); // calls A::A(int, int)
A c = { 0 , 0 }; // calls A::A(std::initializer_list<int>)
A d { 0 , 0 , 0 }; // calls A::A(std::initializer_list<int>)
Conversion -Funktionen können nun mit dem explicit
Spezifizierer explizit gemacht werden.
struct A {
operator bool () const { return true ; }
};
struct B {
explicit operator bool () const { return true ; }
};
A a;
if (a); // OK calls A::operator bool()
bool ba = a; // OK copy-initialization selects A::operator bool()
B b;
if (b); // OK calls B::operator bool()
bool bb = b; // error copy-initialization does not consider B::operator bool()
Alle Mitglieder eines Inline -Namespace werden so behandelt, als wären sie Teil seines übergeordneten Namespace, der die Spezialisierung von Funktionen ermöglicht und den Versionsprozess lockert. Dies ist eine transitive Eigenschaft, wenn A b enthält, das wiederum C und sowohl B als auch C enthält, sind Inline -Namespaces. Cs Mitglieder können so verwendet werden, als wären sie auf A.
namespace Program {
namespace Version1 {
int getVersion () { return 1 ; }
bool isFirstVersion () { return true ; }
}
inline namespace Version2 {
int getVersion () { return 2 ; }
}
}
int version { Program::getVersion ()}; // Uses getVersion() from Version2
int oldVersion { Program::Version1::getVersion ()}; // Uses getVersion() from Version1
bool firstVersion { Program::isFirstVersion ()}; // Does not compile when Version2 is added
Ermöglicht nicht statische Datenmitglieder initialisiert, wo sie deklariert werden, wodurch die Konstrukteure von Standardinitialisierungen potenziell reinigt werden.
// Default initialization prior to C++11
class Human {
Human () : age{ 0 } {}
private:
unsigned age;
};
// Default initialization on C++11
class Human {
private:
unsigned age { 0 };
};
C ++ 11 kann jetzt schließen, wenn eine Reihe von rechtwinkligen Klammern als Operator oder als Schließanweisung von Typedef verwendet wird, ohne Whitespace hinzufügen zu müssen.
typedef std::map< int , std::map < int , std::map < int , int > > > cpp98LongTypedef;
typedef std::map< int , std::map < int , std::map < int , int >>> cpp11LongTypedef;
Mitgliederfunktionen können jetzt qualifiziert werden, je nachdem, ob *this
eine LVALUE- oder RVALUE -Referenz ist.
struct Bar {
// ...
};
struct Foo {
Bar& getBar () & { return bar; }
const Bar& getBar () const & { return bar; }
Bar&& getBar() && { return std::move (bar); }
const Bar&& getBar() const && { return std::move (bar); }
private:
Bar bar;
};
Foo foo{};
Bar bar = foo.getBar(); // calls `Bar& getBar() &`
const Foo foo2{};
Bar bar2 = foo2.getBar(); // calls `Bar& Foo::getBar() const&`
Foo{}.getBar(); // calls `Bar&& Foo::getBar() &&`
std::move (foo).getBar(); // calls `Bar&& Foo::getBar() &&`
std::move (foo2).getBar(); // calls `const Bar&& Foo::getBar() const&`
C ++ 11 ermöglicht Funktionen und Lambdas eine alternative Syntax zum Angeben ihrer Rückgabetypen.
int f () {
return 123 ;
}
// vs.
auto f () -> int {
return 123 ;
}
auto g = []() -> int {
return 123 ;
};
Diese Funktion ist besonders nützlich, wenn bestimmte Rückgabetypen nicht behoben werden können:
// NOTE: This does not compile!
template < typename T, typename U>
decltype (a + b) add(T a, U b) {
return a + b;
}
// Trailing return types allows this:
template < typename T, typename U>
auto add (T a, U b) -> decltype(a + b) {
return a + b;
}
In C ++ 14 kann decltype(auto) (C++14)
stattdessen verwendet werden.
Der noexcept
-Spezifizierer gibt an, ob eine Funktion Ausnahmen auslösen könnte. Es ist eine verbesserte Version von throw()
.
void func1 () noexcept ; // does not throw
void func2 () noexcept ( true ); // does not throw
void func3 () throw(); // does not throw
void func4 () noexcept ( false ); // may throw
Nicht-Wurffunktionen dürfen potenzielle Wurffunktionen aufrufen. Immer wenn eine Ausnahme ausgelöst wird und die Suche nach einem Handler auf den äußersten Block einer Nicht-Wurffunktion stößt, wird die Funktion std :: terate aufgerufen.
extern void f (); // potentially-throwing
void g () noexcept {
f (); // valid, even if f throws
throw 42 ; // valid, effectively a call to std::terminate
}
Bietet Standardtypen zur Darstellung von UTF-8-Zeichenfolgen.
char32_t utf8_str[] = U" u0123 " ;
char16_t utf8_str[] = u" u0123 " ;
C ++ 11 führt einen neuen Weg ein, um String -Literale als "Roh -String -Literale" zu deklarieren. Zeichen, die aus einer Escape -Sequenz ausgegeben werden (Registerkarten, Zeilenfutter, einzelne Backslashes usw.) können während der Aufrechterhaltung der Formatierung roh eingegeben werden. Dies ist zum Beispiel nützlich, um literarischen Text zu schreiben, der viele Zitate oder spezielle Formatierung enthalten kann. Dadurch können Ihre String -Literale leichter zu lesen und zu warten.
Ein Rohstraßenliteral wird unter Verwendung der folgenden Syntax deklariert:
R"delimiter(raw_characters)delimiter"
Wo:
delimiter
ist eine optionale Abfolge von Zeichen aus jedem Quellzeichen außer Klammern, Backslashes und Räumen.raw_characters
ist jede rohe Zeichensequenz; darf die Schließsequenz nicht enthalten ")delimiter"
.Beispiel:
// msg1 and msg2 are equivalent.
const char * msg1 = " n Hello, nt world! n " ;
const char * msg2 = R"(
Hello,
world!
)" ;
std::move
gibt an, dass das an ihn übergebene Objekt seine Ressourcen übertragen kann. Die Verwendung von Objekten, die bewegt wurden, sollte mit Vorsicht verwendet werden, da sie in einem nicht näher bezeichneten Zustand gelassen werden können (siehe: Was kann ich mit einem bewegten Objekt tun?).
Eine Definition von std::move
(eine Bewegung durchzuführen ist nichts anderes, als eine RValue -Referenz zu werfen):
template < typename T>
typename remove_reference<T>::type&& move(T&& arg) {
return static_cast < typename remove_reference<T>::type&&>(arg);
}
Übertragung von std::unique_ptr
s:
std::unique_ptr< int > p1 { new int { 0 }}; // in practice, use std::make_unique
std::unique_ptr< int > p2 = p1; // error -- cannot copy unique pointers
std::unique_ptr< int > p3 = std::move(p1); // move `p1` into `p3`
// now unsafe to dereference object held by `p1`
Gibt die an sie übergebenen Argumente zurück, während ihre Wertkategorie und CV-Qualifizierer beibehalten werden. Nützlich für generische Code und Fabriken. Verwendet in Verbindung mit forwarding references
.
Eine Definition von std::forward
:
template < typename T>
T&& forward( typename remove_reference<T>::type& arg) {
return static_cast <T&&>(arg);
}
Ein Beispiel für eine wrapper
, die nur andere A
-Objekte an eine neue Kopie oder Verschiebung A
Objekts weiterleitet:
struct A {
A () = default ;
A ( const A& o) { std::cout << " copied " << std::endl; }
A (A&& o) { std::cout << " moved " << std::endl; }
};
template < typename T>
A wrapper (T&& arg) {
return A{std::forward<T>(arg)};
}
wrapper (A{}); // moved
A a;
wrapper (a); // copied
wrapper (std::move(a)); // moved
Siehe auch: forwarding references
, rvalue references
.
Die std::thread
-Bibliothek bietet eine Standardmethode, um Threads wie Laichen und Töten zu steuern. Im folgenden Beispiel werden mehrere Threads für verschiedene Berechnungen hervorgebracht, und dann wartet das Programm darauf, dass alle fertig sind.
void foo ( bool clause) { /* do something... */ }
std::vector<std::thread> threadsVector;
threadsVector.emplace_back([]() {
// Lambda function that will be invoked
});
threadsVector.emplace_back(foo, true ); // thread will run foo(true)
for ( auto & thread : threadsVector) {
thread. join (); // Wait for threads to finish
}
Konvertiert ein numerisches Argument in eine std::string
.
std::to_string ( 1.2 ); // == "1.2"
std::to_string ( 123 ); // == "123"
Type-Merkmale definieren eine Kompilierungszeit-Vorlage-basierte Schnittstelle, um die Eigenschaften von Typen abzufragen oder zu ändern.
static_assert (std::is_integral< int >::value);
static_assert (std::is_same< int , int >::value);
static_assert (std::is_same<std::conditional< true , int , double >::type, int >::value);
C ++ 11 führt neue intelligente Zeiger vor: std::unique_ptr
, std::shared_ptr
, std::weak_ptr
. std::auto_ptr
wird nun veraltet und schließlich in C ++ 17 entfernt.
std::unique_ptr
ist ein nicht kopierbarer, beweglicher Zeiger, der seinen eigenen heap-zuallozierten Speicher verwaltet. HINWEIS: Verwenden Sie die Helferfunktionen std::make_X
im Gegensatz zur Verwendung von Konstruktoren. Siehe die Abschnitte für std :: make_unique und std :: make_shared.
std::unique_ptr<Foo> p1 { new Foo{} }; // `p1` owns `Foo`
if (p1) {
p1-> bar ();
}
{
std::unique_ptr<Foo> p2 { std::move (p1)}; // Now `p2` owns `Foo`
f (*p2);
p1 = std::move (p2); // Ownership returns to `p1` -- `p2` gets destroyed
}
if (p1) {
p1-> bar ();
}
// `Foo` instance is destroyed when `p1` goes out of scope
Ein std::shared_ptr
ist ein intelligenter Zeiger, der eine Ressource verwaltet, die über mehrere Eigentümer geteilt wird. Ein gemeinsamer Zeiger enthält einen Steuerblock , der einige Komponenten wie das verwaltete Objekt und einen Referenzschalter enthält. Der gesamte Kontrollblockzugriff ist mit Thread-Safe. Die Manipulation des verwalteten Objekts selbst ist jedoch nicht mit Thread-Safe.
void foo (std::shared_ptr<T> t) {
// Do something with `t`...
}
void bar (std::shared_ptr<T> t) {
// Do something with `t`...
}
void baz (std::shared_ptr<T> t) {
// Do something with `t`...
}
std::shared_ptr<T> p1 { new T{}};
// Perhaps these take place in another threads?
foo (p1);
bar (p1);
baz (p1);
Die Chrono -Bibliothek enthält eine Reihe von Dienstprogrammfunktionen und -typen, die sich mit Dauer , Uhren und Zeitpunkten befassen. Ein Anwendungsfall dieser Bibliothek ist der Benchmarking -Code:
std::chrono::time_point<std::chrono::steady_clock> start, end;
start = std::chrono::steady_clock::now();
// Some computations...
end = std::chrono::steady_clock::now();
std::chrono::duration< double > elapsed_seconds = end - start;
double t = elapsed_seconds.count(); // t number of seconds, represented as a `double`
Tupel sind eine feste Sammlung heterogener Werte. Greifen Sie auf die Elemente eines std::tuple
zu, indem Sie mit std::tie
auspacken oder std::get
.
// `playerProfile` has type `std::tuple<int, const char*, const char*>`.
auto playerProfile = std::make_tuple( 51 , " Frans Nielsen " , " NYI " );
std::get< 0 >(playerProfile); // 51
std::get< 1 >(playerProfile); // "Frans Nielsen"
std::get< 2 >(playerProfile); // "NYI"
Erstellt ein Tupel von Lvalue -Referenzen. Nützlich zum Auspacken von std::pair
und std::tuple
-Objekten. Verwenden Sie std::ignore
als Platzhalter für ignorierte Werte. In C ++ 17 sollten stattdessen strukturierte Bindungen verwendet werden.
// With tuples...
std::string playerName;
std::tie (std::ignore, playerName, std::ignore) = std::make_tuple( 91 , " John Tavares " , " NYI " );
// With pairs...
std::string yes, no;
std::tie (yes, no) = std::make_pair( " yes " , " no " );
std::array
ist ein Container, der auf einem C-Array im C-Stil gebaut ist. Unterstützt gemeinsame Containeroperationen wie Sortieren.
std::array< int , 3 > a = { 2 , 1 , 3 };
std::sort (a.begin(), a.end()); // a == { 1, 2, 3 }
for ( int & x : a) x *= 2 ; // a == { 2, 4, 6 }
Diese Container behalten die durchschnittliche Komplexität der Konstante für die Suche, Einfügung und Entfernen von Vorgängen bei. Um die Komplexität der konstanten Zeit zu erreichen, die Opfernreihenfolge für Geschwindigkeit durch Hashing-Elemente in Eimer. Es gibt vier ungeordnete Behälter:
unordered_set
unordered_multiset
unordered_map
unordered_multimap
std::make_shared
ist die empfohlene Möglichkeit, Instanzen von std::shared_ptr
s aus den folgenden Gründen zu erstellen:
new
Bediener zu benutzen.foo
wie so aufgerufen: foo (std::shared_ptr<T>{ new T{}}, function_that_throws(), std::shared_ptr<T>{ new T{}});
Der Compiler kann new T{}
aufrufen, dann function_that_throws()
und so weiter ... Da wir bei der ersten Konstruktion von A T
Daten auf dem Haufen zugewiesen haben, haben wir hier ein Leck vorgestellt. Mit std::make_shared
erhalten wir eine Ausnahmesicherheit:
foo (std::make_shared<T>(), function_that_throws(), std::make_shared<T>());
std::shared_ptr{ new T{} }
aufrufen, müssen wir Speicher für T
zuweisen, und dann müssen wir im freigegebenen Zeiger Speicher für den Steuerblock im Zeiger zuweisen. Weitere Informationen zu std::unique_ptr
und std::shared_ptr
finden Sie im Abschnitt über intelligente Hinweise.
std::ref(val)
wird verwendet, um ein Objekt vom Typ std::reference_wrapper
zu erstellen, der die Referenz von Val enthält. Wird in Fällen verwendet, wenn übliche Referenzabläufe verwendet werden &
nicht kompiliert werden oder &
wird aufgrund des Typs abgesetzt. std::cref
ist ähnlich, aber erstellte Referenzverpackung hält einen Const Referenz auf VAL.
// create a container to store reference of objects.
auto val = 99 ;
auto _ref = std::ref(val);
_ref++;
auto _cref = std::cref(val);
// _cref++; does not compile
std::vector<std::reference_wrapper< int >>vec; // vector<int&>vec does not compile
vec.push_back(_ref); // vec.push_back(&i) does not compile
cout << val << endl; // prints 100
cout << vec[ 0 ] << endl; // prints 100
cout << _cref; // prints 100
C ++ 11 führt ein Speichermodell für C ++ ein, was bedeutet, dass Bibliotheksunterstützung für Threading- und Atomoperationen bedeutet. Einige dieser Operationen umfassen (aber nicht darauf beschränkt auf) Atomlasten/-speicher, Vergleich und Swap, Atomflaggen, Versprechen, Futures, Schlösser und Zustandsvariablen.
Siehe die Abschnitte auf: std :: thread
std::async
führt die angegebene Funktion entweder asynchron oder träge aus, und gibt dann eine std::future
zurück, die das Ergebnis dieses Funktionsaufrufs enthält.
Der erste Parameter ist die Richtlinie, die sein kann:
std::launch::async | std::launch::deferred
Es liegt an der Implementierung, ob eine asynchrone Ausführung oder eine faule Bewertung durchgeführt werden soll.std::launch::async
Führen Sie das Callable -Objekt in einem neuen Thread aus.std::launch::deferred
Lazy Evaluation auf dem aktuellen Thread durchführen. int foo () {
/* Do something here, then return the result. */
return 1000 ;
}
auto handle = std::async(std::launch::async, foo); // create an async task
auto result = handle.get(); // wait for the result
std::begin
und std::end
Kostenlose Funktionen wurden hinzugefügt, um die Iteratoren eines Containers allgemein zurückzukehren und zu beenden. Diese Funktionen funktionieren auch mit RAW -Arrays, die keine begin
und end
haben.
template < typename T>
int CountTwos ( const T& container) {
return std::count_if ( std::begin (container), std::end (container), []( int item) {
return item == 2 ;
});
}
std::vector< int > vec = { 2 , 2 , 43 , 435 , 4543 , 534 };
int arr[ 8 ] = { 2 , 43 , 45 , 435 , 32 , 32 , 32 , 32 };
auto a = CountTwos(vec); // 2
auto b = CountTwos(arr); // 1
Anthony Calandra
Siehe: https://github.com/anthonycalandra/modern-cpp-features/graphs/contributors
MIT