Namespaces
Variants

Placeholder type specifiers (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const / volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Un spécificateur de type d'espace réservé désigne un type d'espace réservé qui sera remplacé ultérieurement, généralement par déduction d'un initialiseur .

Table des matières

Syntaxe

type-constraint  (optionnel) auto (1)
type-constraint  (optionnel) decltype(auto) (2) (depuis C++14)
type-constraint - (depuis C++20) un nom de concept , éventuellement qualifié, suivi optionnellement d'une liste d'arguments template entre <>
1) Le type est déduit en utilisant les règles pour template argument deduction .
2) Le type est decltype(expr) , où expr est l'initialiseur ou les opérandes utilisés dans les instructions return .

Le placeholder auto peut être accompagné de modificateurs, tels que const ou & , qui participeront à la déduction de type. Le placeholder decltype ( auto ) doit être le seul constituant du type déclaré. (depuis C++14)

Si type-constraint est présent, soit T le type déduit pour le placeholder, la type-constraint introduit une expression de contrainte comme suit :

  • Si type-constraint est Concept<A 1 , ..., A n > , alors l'expression de contrainte est Concept<T, A 1 , ..., A n > ;
  • sinon ( type-constraint est Concept sans liste d'arguments), l'expression de contrainte est Concept<T> .

La déduction échoue si l'expression de contrainte est invalide ou retourne false .

(depuis C++20)

Explication

Un spécificateur de type d'espace réservé peut apparaître dans les contextes suivants :

Déclarations de paramètres

Dans les déclarations de paramètres suivantes, le type du paramètre déclaré peut être de syntaxe (1) :

  • Si un paramètre d'une expression lambda a un type d'espace réservé, l'expression lambda est un lambda générique.
(depuis C++14)
(depuis C++17)
(depuis C++20)

Déclarations de fonctions

Un type d'espace réservé peut apparaître dans les spécificateurs de déclaration pour un déclarateur de fonction qui inclut un type de retour en suffixe.

Un type d'espace réservé peut apparaître dans les spécificateurs de déclaration ou les spécificateurs de type dans le type de retour déclaré d'un déclarateur de fonction . La déduction du type de retour sera appliquée dans ce cas.

(depuis C++14)
auto f() -> int; // OK : f retourne int
auto g() { return 0.0; } // OK depuis C++14 : g retourne double
auto h(); // OK depuis C++14 : le type de retour de h sera déduit lors de sa définition

Déclarations de variables

Le type d'une variable déclarée en utilisant un type de substitution est déduit de son initialiseur . Cette utilisation est autorisée dans une déclaration d'initialisation d'une variable.

Le type de substitution ne peut apparaître que comme l'un des spécificateurs de déclaration dans la séquence de spécificateurs de déclaration ou comme l'un des spécificateurs de type dans un type de retour final qui spécifie le type remplaçant un tel spécificateur de déclaration. Dans ce cas, la déclaration doit déclarer au moins une variable, et chaque variable doit avoir un initialiseur non vide.

// Les "auto" dans les spécificateurs de déclaration
auto x = 5; // OK : x a le type int
const auto *v = &x, u = 6; // OK : v a le type const int*, u a le type const int
static auto y = 0.0; // OK : y a le type double
auto f() -> int;
auto (*fp)() -> auto = f; // OK : le "auto" dans le type de retour postfixé
                          // peut être déduit de f

Déclarations de liaisons structurées

Le spécificateur auto peut être utilisé dans une déclaration de liaison structurée .

(depuis C++17)

new expressions

Un type d'espace réservé peut être utilisé dans la séquence de spécificateurs de type du type-id d'une expression new . Dans un tel type-id, le type d'espace réservé doit apparaître comme l'un des spécificateurs de type dans la séquence de spécificateurs de type ou un type de retour trailing qui spécifie le type remplaçant un tel spécificateur de type.

Cast de style fonction

Le spécificateur de type auto peut être utilisé comme spécificateur de type d'un cast de style fonction .

(depuis C++23)

Notes

Jusqu'à C++11, auto avait la sémantique d'un spécificateur de durée de stockage .

Un programme qui utilise un type d'espace réservé dans un contexte non explicitement indiqué ci-dessus est non conforme.

Si une déclaration déclare plusieurs entités, et que la séquence de spécificateurs de déclaration utilise un type d'espace réservé, le programme est mal formé si l'une des conditions suivantes est satisfaite :

  • Certaines des entités déclarées ne sont pas des variables.
  • Le type qui remplace le type de substitution n'est pas le même dans chaque déduction.
auto f() -> int, i = 0; // Erreur : déclare une fonction et une variable avec "auto"
auto a = 5, b = {1, 2}; // Erreur : types différents pour "auto"

Si une fonction ou une variable avec un type d'espace réservé non remplacé est référencée par une expression, le programme est mal formé.

auto v = 1;
auto l = [&]
{
    v++;
    return l;// Erreur : Le type d'espace réservé pour l n'a pas été remplacé
};
std::function<void()> p = [&]
{
    v++;
    return p;// Correct
};

Le mot-clé auto peut également être utilisé dans un spécificateur de nom imbriqué. Un spécificateur de nom imbriqué de la forme auto :: est un espace réservé qui est remplacé par un type de classe ou d'énumération suivant les règles de déduction de type contraint pour les espaces réservés.

(concepts TS)
Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_decltype_auto 201304L (C++14) decltype ( auto )

Mots-clés

auto , decltype

Exemple

#include <iostream>
#include <utility>
template<class T, class U>
auto add(T t, U u) { return t + u; } // le type de retour est le type de operator+(T, U)
// la transmission parfaite d'un appel de fonction doit utiliser decltype(auto)
// au cas où la fonction appelée retourne par référence
template<class F, class... Args>
decltype(auto) PerfectForward(F fun, Args&&... args) 
{ 
    return fun(std::forward<Args>(args)...); 
}
template<auto n> // déclaration de paramètre auto C++17
auto f() -> std::pair<decltype(n), decltype(n)> // auto ne peut pas déduire d'une liste d'initialisation entre accolades
{
    return {n, n};
}
int main()
{
    auto a = 1 + 2;          // le type de a est int
    auto b = add(1, 1.2);    // le type de b est double
    static_assert(std::is_same_v<decltype(a), int>);
    static_assert(std::is_same_v<decltype(b), double>);
    auto c0 = a;             // le type de c0 est int, contenant une copie de a
    decltype(auto) c1 = a;   // le type de c1 est int, contenant une copie de a
    decltype(auto) c2 = (a); // le type de c2 est int&, un alias de a
    std::cout << "avant modification via c2, a = " << a << '\n';
    ++c2;
    std::cout << " après modification via c2, a = " << a << '\n';
    auto [v, w] = f<0>(); // déclaration de liaison structurée
    auto d = {1, 2}; // OK : le type de d est std::initializer_list<int>
    auto n = {5};    // OK : le type de n est std::initializer_list<int>
//  auto e{1, 2};    // Erreur depuis DR n3922, std::initializer_list<int> avant
    auto m{5};       // OK : le type de m est int depuis DR n3922, initializer_list<int> avant
//  decltype(auto) z = { 1, 2 } // Erreur : {1, 2} n'est pas une expression
    // auto est couramment utilisé pour les types anonymes comme les types d'expressions lambda
    auto lambda = [](int x) { return x + 3; };
//  auto int x; // valide en C++98, erreur depuis C++11
//  auto x;     // valide en C, erreur en C++
    [](...){}(c0, c1, v, w, d, n, m, lambda); // supprime les avertissements "variable inutilisée"
}

Sortie possible :

avant modification via c2, a = 3
 après modification via c2, a = 4

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement publié Comportement corrigé
CWG 1265 C++11 le spécificateur auto pouvait être utilisé pour déclarer une fonction avec un type de retour
trailing et définir une variable dans une même instruction de déclaration
interdit
CWG 1346 C++11 une liste d'expressions entre parenthèses ne pouvait pas être assignée à une
variable auto
autorisé
CWG 1347 C++11 une déclaration avec le spécificateur auto pouvait définir deux variables
avec les types T et std:: initializer_list < T > respectivement
interdit
CWG 1852 C++14 le spécificateur auto dans decltype ( auto ) était aussi un placeholder pas un placeholder
dans ce cas
CWG 1892 C++11 le type de retour d'un type-id de pointeur de fonction pouvait être auto interdit
CWG 2476 C++11 la résolution de CWG issue 1892 interdisait la déduction
du type de retour des variables de pointeur de fonction depuis les initialiseurs
autorisé
N3922 C++11 la direct-list-initialization de auto déduit std::initializer_list ill-formed pour plus d'un
élément, déduit le type d'élément
pour un seul élément

Références

  • Norme C++23 (ISO/CEI 14882:2024) :
  • 9.2.9.6 Spécificateurs de type substitut [dcl.spec.auto]
  • Norme C++20 (ISO/IEC 14882:2020) :
  • 9.2.8.5 Spécificateurs de type substitut [dcl.spec.auto]
  • Norme C++17 (ISO/CEI 14882:2017) :
  • 10.1.7.4 Le spécificateur auto [dcl.spec.auto]
  • Norme C++14 (ISO/CEI 14882:2014) :
  • 7.1.6.4 auto spécificateur [dcl.spec.auto]
  • Norme C++11 (ISO/CEI 14882:2011) :
  • 7.1.6.4 auto spécificateur [dcl.spec.auto]