Platzhalter-Typspezifizierer (seit C++11)
Ein Platzhalter-Typspezifizierer bezeichnet einen Platzhaltertyp, der später ersetzt wird, typischerweise durch Ableitung von einem Initialisierer.
Inhalt |
[bearbeiten] Syntax
type-constraint (optional) auto |
(1) | ||||||||
type-constraint (optional) decltype(auto) |
(2) | (seit C++14) | |||||||
| type-constraint | - | (seit C++20) ein Konzeptname, optional qualifiziert, optional gefolgt von einer Template-Argumentliste in <> eingeschlossen |
decltype(expr), wobei expr der Initialisierer oder die in Rückgabewerten verwendeten sind.Der Platzhalter auto kann von Modifikatoren wie const oder & begleitet werden, die an der Typableitung teilnehmen. Der Platzhalter decltype(auto) muss der einzige Bestandteil des deklarierten Typs sein.(seit C++14)
|
Wenn type-constraint vorhanden ist, sei
Die Ableitung schlägt fehl, wenn der Constraint-Ausdruck ungültig ist oder false zurückgibt. |
(seit C++20) |
[bearbeiten] Erklärung
Ein Platzhalter-Typspezifizierer kann in folgenden Kontexten auftreten:
ParameterdeklarationenIn den folgenden Parameterdeklarationen kann der Typ des deklarierten Parameters den Syntax (1) haben.
|
(seit C++14) |
|
(seit C++17) |
|
(seit C++20) |
[bearbeiten] Funktionsdeklarationen
Ein Platzhaltertyp kann in den Deklarationsspezifizierern für einen Funktionsdeklarator erscheinen, der einen nachgestellten Rückgabetyp enthält.
|
Ein Platzhaltertyp kann in den Deklarationsspezifizierern oder Typspezifizierern im deklarierten Rückgabetyp eines Funktionsdeklarators erscheinen. Rückgabetypableitung wird in diesem Fall angewendet. |
(seit C++14) |
auto f() -> int; // OK: f returns int auto g() { return 0.0; } // OK since C++14: g returns double auto h(); // OK since C++14: h’s return type will be deduced when it is defined
[bearbeiten] Variablendeklarationen
Der Typ einer Variablen, die mit einem Platzhaltertyp deklariert wird, wird aus ihrem Initialisierer abgeleitet. Diese Verwendung ist in einer initialisierenden Deklaration einer Variablen zulässig.
Der Platzhaltertyp kann nur als einer der Deklarationsspezifizierer in der Sequenz der Deklarationsspezifizierer oder als einer der Typspezifizierer in einem nachgestellten Rückgabetyp erscheinen, der den Typ angibt, der einen solchen Deklarationsspezifizierer ersetzt. In diesem Fall muss die Deklaration mindestens eine Variable deklarieren, und jede Variable muss einen nicht leeren Initialisierer haben.
// “auto”s in declaration specifiers auto x = 5; // OK: x has type int const auto *v = &x, u = 6; // OK: v has type const int*, u has type const int static auto y = 0.0; // OK: y has type double auto f() -> int; auto (*fp)() -> auto = f; // OK: the “auto” in the trailing return type // can be deduced from f
Strukturelle BindungsdeklarationenDer auto-Spezifizierer kann in einer strukturellen Bindungsdeklaration verwendet werden. |
(seit C++17) |
[bearbeiten] new-Ausdrücke
Ein Platzhaltertyp kann in der Typspezifizierersequenz des Typ-IDs eines new-Ausdrucks verwendet werden. In einem solchen Typ-ID muss der Platzhaltertyp als einer der Typspezifizierer in der Typspezifizierersequenz oder als nachgestellter Rückgabetyp erscheinen, der den Typ angibt, der einen solchen Typspezifizierer ersetzt.
Funktionsstil-CastDer auto-Typspezifizierer kann als Typspezifizierer eines Funktionsstil-Casts verwendet werden. |
(seit C++23) |
[bearbeiten] Hinweise
Bis C++11 hatte auto die Semantik eines Speicherdauer-Spezifizierers.
Ein Programm, das einen Platzhaltertyp in einem Kontext verwendet, der nicht explizit angegeben ist, ist fehlerhaft.
Wenn eine Deklaration mehrere Entitäten deklariert und die Sequenz der Deklarationsspezifizierer einen Platzhaltertyp verwendet, ist das Programm fehlerhaft, wenn eine der folgenden Bedingungen erfüllt ist:
- Einige der deklarierten Entitäten sind keine Variablen.
- Der Typ, der den Platzhaltertyp ersetzt, ist bei jeder Ableitung nicht derselbe.
auto f() -> int, i = 0; // Error: declares a function and a variable with “auto” auto a = 5, b = {1, 2}; // Error: different types for “auto”
|
Das Schlüsselwort auto kann auch in einem verschachtelten Namensspezifizierer verwendet werden. Ein verschachtelter Namensspezifizierer der Form auto:: ist ein Platzhalter, der durch einen Klassen- oder Enumerationstyp gemäß den Regeln für die Ableitung von eingeschränkten Typ-Platzhaltern ersetzt wird. |
(Konzepte TS) |
| Feature-Testmakro | Wert | Std | Feature |
|---|---|---|---|
__cpp_decltype_auto |
201304L |
(C++14) | decltype(auto) |
[bearbeiten] Schlüsselwörter
[bearbeiten] Beispiel
#include <iostream> #include <utility> template<class T, class U> auto add(T t, U u) { return t + u; } // the return type is the type of operator+(T, U) // perfect forwarding of a function call must use decltype(auto) // in case the function it calls returns by reference template<class F, class... Args> decltype(auto) PerfectForward(F fun, Args&&... args) { return fun(std::forward<Args>(args)...); } template<auto n> // C++17 auto parameter declaration auto f() -> std::pair<decltype(n), decltype(n)> // auto can't deduce from brace-init-list { return {n, n}; } int main() { auto a = 1 + 2; // type of a is int auto b = add(1, 1.2); // type of b is double static_assert(std::is_same_v<decltype(a), int>); static_assert(std::is_same_v<decltype(b), double>); auto c0 = a; // type of c0 is int, holding a copy of a decltype(auto) c1 = a; // type of c1 is int, holding a copy of a decltype(auto) c2 = (a); // type of c2 is int&, an alias of a std::cout << "before modification through c2, a = " << a << '\n'; ++c2; std::cout << " after modification through c2, a = " << a << '\n'; auto [v, w] = f<0>(); //structured binding declaration auto d = {1, 2}; // OK: type of d is std::initializer_list<int> auto n = {5}; // OK: type of n is std::initializer_list<int> // auto e{1, 2}; // Error as of DR n3922, std::initializer_list<int> before auto m{5}; // OK: type of m is int as of DR n3922, initializer_list<int> before // decltype(auto) z = { 1, 2 } // Error: {1, 2} is not an expression // auto is commonly used for unnamed types such as the types of lambda expressions auto lambda = [](int x) { return x + 3; }; // auto int x; // valid C++98, error as of C++11 // auto x; // valid C, error in C++ [](...){}(c0, c1, v, w, d, n, m, lambda); // suppresses "unused variable" warnings }
Mögliche Ausgabe
before modification through c2, a = 3 after modification through c2, a = 4
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 1265 | C++11 | der auto-Spezifizierer konnte verwendet werden, um eine Funktion mit einem nachgestellten Rückgabetyp zu deklarieren und eine Variable in einer Deklarationsanweisung zu definieren |
verboten |
| CWG 1346 | C++11 | eine geklammerte Ausdrucksliste konnte keiner auto-Variable zugewiesen werden | erlaubt |
| CWG 1347 | C++11 | eine Deklaration mit dem auto-Spezifizierer konnte zwei Variablen definieren mit den Typen T bzw. std::initializer_list<T> |
verboten |
| CWG 1852 | C++14 | der Rückgabetyp von decltype(auto) war ebenfalls ein Platzhalter | kein Platzhalter in diesem Fall |
| CWG 1892 | C++11 | der Rückgabetyp eines Funktionszeiger-Typ-IDs konnte auto sein | verboten |
| CWG 2476 | C++11 | die Auflösung von CWG Issue 1892 verbot die Ableitung des Rückgabetyps von Funktionszeigervariablen aus Initialisierern |
erlaubt |
[bearbeiten] Referenzen
- C++23 Standard (ISO/IEC 14882:2024)
- 9.2.9.6 Platzhalter-Typspezifizierer [dcl.spec.auto]
- C++20 Standard (ISO/IEC 14882:2020)
- 9.2.8.5 Platzhalter-Typspezifizierer [dcl.spec.auto]
- C++17 Standard (ISO/IEC 14882:2017)
- 10.1.7.4 Der
auto-Spezifizierer [dcl.spec.auto]
- 10.1.7.4 Der
- C++14 Standard (ISO/IEC 14882:2014)
- 7.1.6.4
auto-Spezifizierer [dcl.spec.auto]
- 7.1.6.4
- C++11 Standard (ISO/IEC 14882:2011)
- 7.1.6.4
auto-Spezifizierer [dcl.spec.auto]
- 7.1.6.4