Direct-initialization
Initialise un objet à partir d'un ensemble explicite d'arguments de constructeur.
Table des matières |
Syntaxe
T
objet
(
arg
);
T
objet
|
(1) | ||||||||
T
objet
{
arg
};
|
(2) | (depuis C++11) | |||||||
T
(
autre
)
T
|
(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 :
Les effets de l'initialisation directe sont :
-
Si
Test un type tableau,
|
(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
Test un type de classe,
|
(depuis C++17) |
-
-
les constructeurs de
Tsont examinés et la meilleure correspondance est sélectionnée par résolution de surcharge. Le constructeur est ensuite appelé pour initialiser l'objet.
-
les constructeurs de
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
Test 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
Test 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