Namespaces
Variants

Direct-initialization

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

Initialise un objet à partir d'un ensemble explicite d'arguments de constructeur.

Table des matières

Syntaxe

T objet ( arg );

T objet ( arg1, arg2, ... );

(1)
T objet { arg }; (2) (depuis C++11)
T ( autre )

T ( arg1, arg2, ... )

(3)
static_cast< T >( autre ) (4)
new T ( args, ... ) (5)
Classe :: Classe () : membre ( args, ... ) { ... } (6)
[ arg ]() { ... } (7) (depuis C++11)

Explication

L'initialisation directe est effectuée dans les situations suivantes :

1) Initialisation avec une liste d'expressions entre parenthèses non vide ou des listes d'initialisation entre accolades (depuis C++11) .
2) Initialisation d'un objet de type non-classe avec un seul initialiseur entre accolades (note : pour les types classe et autres utilisations de braced-init-list, voir list-initialization ) (since C++11) .
3) Initialisation de un temporaire prvalue (jusqu'au C++17) l'objet résultat d'une prvalue (depuis C++17) par conversion de type fonction ou avec une liste d'expressions entre parenthèses.
4) Initialisation de un temporaire prvalue (jusqu'à C++17) l'objet résultat d'une prvalue (depuis C++17) par une expression static_cast .
5) Initialisation d'un objet avec une durée de stockage dynamique par une expression new avec un initialiseur.
6) Initialisation d'une base ou d'un membre non statique par la liste d'initialisation du constructeur initializer list .
7) Initialisation des membres de l'objet de fermeture à partir des variables capturées par copie dans une expression lambda.

Les effets de l'initialisation directe sont :

  • Si T est un type tableau,
  • le programme est mal formé.
(jusqu'à C++20)
struct A
{
    explicit A(int i = 0) {}
};
A a[2](A(1)); // OK: initializes a[0] with A(1) and a[1] with A()
A b[2]{A(1)}; // error: implicit copy-list-initialization of b[1]
              //        from {} selected explicit constructor
(depuis C++20)
  • Si T est un type de classe,
  • si l'initialiseur est une expression prvalue dont le type est le même que la classe T (en ignorant les qualificatifs cv), l'expression d'initialisation elle-même, plutôt qu'un temporaire matérialisé à partir de celle-ci, est utilisée pour initialiser l'objet de destination.
    (Avant C++17, le compilateur pouvait élider la construction à partir du temporaire prvalue dans ce cas, mais le constructeur approprié devait toujours être accessible : voir copy elision )
(depuis C++17)
  • les constructeurs de T sont examinés et la meilleure correspondance est sélectionnée par résolution de surcharge. Le constructeur est ensuite appelé pour initialiser l'objet.
  • sinon, si le type de destination est une classe agrégée (éventuellement qualifiée cv), elle est initialisée comme décrit dans l'initialisation d'agrégat sauf que les conversions rétrécissantes sont autorisées, les initialiseurs désignés ne sont pas permis, un temporaire lié à une référence ne voit pas sa durée de vie étendue, il n'y a pas d'élision d'accolades, et tout élément sans initialiseur est initialisé par valeur .
struct B
{
    int a;
    int&& r;
};
int f();
int n = 10;
B b1{1, f()};            // OK, lifetime is extended
B b2(1, f());            // well-formed, but dangling reference
B b3{1.0, 1};            // error: narrowing conversion
B b4(1.0, 1);            // well-formed, but dangling reference
B b5(1.0, std::move(n)); // OK
(depuis C++20)
  • Sinon, si T est un type non-classe mais que le type source est un type classe, les fonctions de conversion du type source et de ses classes de base, le cas échéant, sont examinées et la meilleure correspondance est sélectionnée par résolution de surcharge. La conversion utilisateur sélectionnée est ensuite utilisée pour convertir l'expression d'initialisation en l'objet en cours d'initialisation.
  • Sinon, si T est bool et que le type source est std::nullptr_t , la valeur de l'objet initialisé est false .
  • Sinon, les conversions standard sont utilisées, si nécessaire, pour convertir la valeur de other en la version non qualifiée cv de T , et la valeur initiale de l'objet en cours d'initialisation est la valeur (éventuellement convertie).

Notes

L'initialisation directe est plus permissive que l'initialisation par copie : l'initialisation par copie ne considère que les constructeurs non- explicit et les conversion functions utilisateur non-explicites, tandis que l'initialisation directe considère tous les constructeurs et toutes les fonctions de conversion utilisateur.

En cas d'ambiguïté entre une déclaration de variable utilisant la syntaxe d'initialisation directe (1) (avec des parenthèses rondes) et une déclaration de fonction , le compilateur choisit toujours la déclaration de fonction. Cette règle de désambiguïsation est parfois contre-intuitive et a été appelée le most vexing parse .

#include <fstream>
#include <iterator>
#include <string>
int main()
{
    std::ifstream file("data.txt");
    // Ce qui suit est une déclaration de fonction :
    std::string foo1(std::istreambuf_iterator<char>(file),
                     std::istreambuf_iterator<char>());
    // Cela déclare une fonction appelée foo1, dont le type de retour est std::string,
    // le premier paramètre a le type std::istreambuf_iterator<char> et le nom "file",
    // le second paramètre n'a pas de nom et a le type std::istreambuf_iterator<char>(),
    // qui est réécrit en type pointeur de fonction std::istreambuf_iterator<char>(*)()
    // Solution pré-C++11 (pour déclarer une variable) - ajouter des parenthèses supplémentaires autour d'un
    // des arguments :
    std::string str1((std::istreambuf_iterator<char>(file)),
                      std::istreambuf_iterator<char>());
    // Solution post-C++11 (pour déclarer une variable) - utiliser l'initialisation par liste pour n'importe quel
    // des arguments :
    std::string str2(std::istreambuf_iterator<char>{file}, {});
}

Exemple

#include <iostream>
#include <memory>
#include <string>
struct Foo
{
    int mem;
    explicit Foo(int n) : mem(n) {}
};
int main()
{
    std::string s1("test"); // constructeur depuis const char*
    std::string s2(10, 'a');
    std::unique_ptr<int> p(new int(1));  // OK : les constructeurs explicites sont autorisés
//  std::unique_ptr<int> p = new int(1); // erreur : le constructeur est explicite
    Foo f(2); // f est initialisé directement :
              // le paramètre n du constructeur est initialisé par copie depuis la rvalue 2
              // f.mem est initialisé directement depuis le paramètre n
//  Foo f2 = 2; // erreur : le constructeur est explicite
    std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem  << '\n';
}

Sortie :

test aaaaaaaaaa 1 2

Voir aussi