Namensräume
Varianten
Aktionen

Platzhalter-Typspezifizierer (seit C++11)

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

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
1) Der Typ wird nach den Regeln der Template-Argument-Ableitung abgeleitet.
2) Der Typ ist 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 T der für den Platzhalter abgeleitete Typ, dann führt type-constraint einen Constraint-Ausdruck wie folgt ein

  • Wenn type-constraint Concept<A1, ..., An> ist, dann ist der Constraint-Ausdruck Concept<T, A1, ..., An>;
  • andernfalls (wenn type-constraint Concept ohne Argumentliste ist), ist der Constraint-Ausdruck Concept<T>.

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:

Parameterdeklarationen

In den folgenden Parameterdeklarationen kann der Typ des deklarierten Parameters den Syntax (1) haben.

  • Wenn ein Parameter eines Lambda-Ausdrucks einen Platzhaltertyp hat, ist der Lambda-Ausdruck ein generischer Lambda.
(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 Bindungsdeklarationen

Der 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-Cast

Der 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

auto, decltype

[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]
  • C++14 Standard (ISO/IEC 14882:2014)
  • 7.1.6.4 auto-Spezifizierer [dcl.spec.auto]
  • C++11 Standard (ISO/IEC 14882:2011)
  • 7.1.6.4 auto-Spezifizierer [dcl.spec.auto]