Qu'est-ce qu'un constructeur de conversion en C++? C'est pour quoi?
j'ai entendu dire que C++ a quelque chose appelé "convertion constructors" ou "converting constructors". Quelles sont-elles, et quels sont-ils? Je l'ai vu mentionné en ce qui concerne ce code:
class MyClass
{
public:
int a, b;
MyClass( int i ) {}
}
int main()
{
MyClass M = 1 ;
}
2 réponses
La définition d'un conversion constructeur est différent entre C++03 et C++11. Dans les deux cas, il doit s'agir d'un non-explicit
constructor (sinon il ne serait pas impliqué dans les conversions implicites), mais pour C++03 il doit aussi être callable avec un seul argument. Qui est:
struct foo
{
foo(int x); // 1
foo(char* s, int x = 0); // 2
foo(float f, int x); // 3
explicit foo(char x); // 4
};
les constructeurs 1 et 2 convertissent tous deux des constructeurs en C++03 et C++11. Le constructeur 3, qui doit prendre deux arguments, n'est qu'un constructeur en conversion en C++11. La dernière, le constructeur 4 n'est pas un constructeur en conversion car il est explicit
.
C++03: §12.3.1
Un constructeur déclaré sans spécificateur de fonction
explicit
qui peut être appelée avec un seul paramètre spécifie une conversion du type de son premier paramètre le type de sa classe. Un tel constructeur est appelé une conversion constructeur.C++11: §12.3.1
Un constructeur déclaré sans spécificateur de fonction
explicit
spécifie une conversion des types de ses paramètres au type de sa classe. Un tel constructeur est appelé un constructeur de conversion.
pourquoi les constructeurs avec plus d'un paramètre sont-ils considérés comme des constructeurs en conversion en C++11? Que est parce que le nouveau standard nous fournit une syntaxe pratique pour passer des arguments et retourner des valeurs en utilisant moisa-init-listes. Considérons l'exemple suivant:
foo bar(foo f)
{
return {1.0f, 5};
}
La possibilité de spécifier la valeur de retour comme un liste d'entrées-sorties est considéré comme une conversion. Cela utilise le constructeur de conversion pour foo
qui prend un float
et int
. En outre, nous pouvons appeler cette fonction en faisant bar({2.5f, 10})
. C'est aussi une conversion. Comme il s'agit de conversions, il est logique que les constructeurs qu'ils utilisent soient conversion des constructeurs.
Il est donc important de noter que le constructeur de foo
ce qui prend un float
et int
explicit
le spécificateur de fonction empêcherait le code ci-dessus de compiler. La nouvelle syntaxe ci-dessus ne peut être utilisée que s'il y a un constructeur de conversion disponible pour faire le travail.
C++11: §6.6.3:
return
déclaration liste d'entrées-sorties initialise l'objet ou la référence à retourner de la fonction par copy-list-initialization (8.5.4) à partir de la liste d'initialiseur spécifiée.§8.5:
L'initialisation qui se produit [...] dans sa plaidoirie, en passant [...] est appelée la copie de l'initialisation.
§12.3.1:
Un constructeur explicite construit des objets comme des constructeurs non-explicites, mais ne le fait que lorsque la syntaxe d'initialisation directe (8.5) ou les casts (5.2.9, 5.4) sont explicitement utilisés.
conversion implicite avec conversion du constructeur
prenons l'exemple de la question plus complexe
class MyClass
{
public:
int a, b;
MyClass( int i ) {}
MyClass( const char* n, int k = 0 ) {}
MyClass( MyClass& obj ) {}
}
les deux premiers constructeurs convertissent les constructeurs. Le troisième est un constructeur de copie, et en tant que tel c'est un autre constructeur de conversion.
un constructeur en conversion permet la conversion implicite du type argument au type constructeur. Ici, le premier constructeur permet la conversion d'un int
pour un objet de la classe MyClass
. Deuxième constructeur permet la conversion d'une chaîne à un objet de classe MyClass
. Et la troisième... à partir d'un objet de classe MyClass
à un objet de classe MyClass
!
pour être un constructeur en Conversion, le constructeur doit avoir un seul argument (dans le second, le second argument a une valeur par défaut) et être déclaré sans mot-clé explicit
.
alors, l'initialisation dans main peut ressembler à ceci:
int main()
{
MyClass M = 1 ;
// which is an alternative to
MyClass M = MyClass(1) ;
MyClass M = "super" ;
// which is an alternative to
MyClass M = MyClass("super", 0) ;
// or
MyClass M = MyClass("super") ;
}
mot clé Explicit et les constructeurs
maintenant, et si nous avions utilisé le explicit
mot clé ?
class MyClass
{
public:
int a, b;
explicit MyClass( int i ) {}
}
alors, le compilateur n'accepterait pas
int main()
{
MyClass M = 1 ;
}
puisqu'il s'agit d'une conversion implicite. Au lieu de cela, écrire
int main()
{
MyClass M(1) ;
MyClass M = MyClass(1) ;
MyClass* M = new MyClass(1) ;
MyClass M = (MyClass)1;
MyClass M = static_cast<MyClass>(1);
}
explicit
mot-clé doit toujours être utilisé pour empêcher la conversion implicite pour un constructeur et il s'applique au constructeur dans une déclaration de classe.