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 ;
}
35
demandé sur Mooing Duck 2013-02-26 02:09:36

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 fonctionexplicit 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 fonctionexplicit 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 intexplicit 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.

43
répondu Joseph Mansfield 2013-02-25 22:52:43

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.

12
répondu octoback 2013-02-25 22:16:55