Pregunta ¿Qué es un constructor de conversión en C ++? ¿Para qué sirve?


He oído que C ++ tiene algo llamado "constructores de conversión" o "constructores de conversión". ¿Qué son estos y para qué sirven? Lo vi mencionado con respecto a este código:

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
}

 int main()
{
    MyClass M = 1 ;
}

34
2018-02-25 22:09


origen


Respuestas:


La definición de convertidor constructor es diferente entre C ++ 03 y C ++ 11. En ambos casos, debe ser un noexplicit constructor (de lo contrario, no estaría involucrado en conversiones implícitas), pero para C ++ 03 también debe ser invocable con un solo argumento. Es decir:

struct foo
{
  foo(int x);              // 1
  foo(char* s, int x = 0); // 2
  foo(float f, int x);     // 3
  explicit foo(char x);    // 4
};

Los constructores 1 y 2 son constructores de conversión en C ++ 03 y C ++ 11. Constructor 3, que debe tomar dos argumentos, es solo un constructor de conversión en C ++ 11. El último, el constructor 4, no es un constructor de conversión porque es explicit.

  • C ++ 03: §12.3.1

    Un constructor declarado sin el especificador de funciones  explicit que se puede invocar con un solo parámetro especifica una conversión del tipo de su primer parámetro al tipo de su clase. Tal constructor se llama constructor de conversión.

  • C ++ 11: §12.3.1

    Un constructor declarado sin el especificador de funciones  explicit especifica una conversión de los tipos de sus parámetros al tipo de su clase. Tal constructor se llama constructor de conversión.

¿Por qué los constructores con más de un parámetro se consideran convertidores en C ++ 11? Esto se debe a que el nuevo estándar nos proporciona una útil sintaxis para pasar argumentos y devolver valores usando listas de inicio reforzado. Considere el siguiente ejemplo:

foo bar(foo f)
{
  return {1.0f, 5};
}

La capacidad de especificar el valor de retorno como una braced-init-list se considera una conversión. Esto usa el constructor de conversión para foo eso toma una float y un int. Además, podemos llamar a esta función haciendo bar({2.5f, 10}). Esta es también una conversión. Ya que son conversiones, tiene sentido para los constructores que solían ser Conversión de constructores.

Es importante tener en cuenta, por lo tanto, que hacer el constructor de foo que toma una float y un int tener el explicit el especificador de función detendría la compilación del código anterior. La nueva sintaxis anterior solo se puede usar si hay un constructor de conversión disponible para hacer el trabajo.

  • C ++ 11: §6.6.3:

    UN return declaración con un braced-init-list inicializa el objeto o la referencia que se devolverá desde la función mediante copy-list-initialization (8.5.4) desde la lista de inicializadores especificada.

    §8.5:

    La inicialización que ocurre en el paso de [...] argumentos se denomina inicialización de copia.

    §12.3.1:

    Un constructor explícito construye objetos al igual que los constructores no explícitos, pero solo lo hace cuando se usan explícitamente la sintaxis de inicialización directa (8.5) o los moldes (5.2.9, 5.4).


42
2018-02-25 22:31



Conversión implícita con el constructor de conversión

Hagamos que el ejemplo en la pregunta sea más complejo

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
     MyClass( const char* n, int k = 0 ) {}
     MyClass( MyClass& obj ) {}
}

Los primeros dos constructores están convirtiendo constructores. El tercero es un constructor de copia, y como tal es otro constructor de conversión.

Un constructor de conversión permite la conversión implícita del tipo de argumento al tipo de constructor. Aquí, el primer constructor permite la conversión de un int a un objeto de clase MyClass. El segundo constructor permite la conversión de una cadena a un objeto de clase MyClass. Y tercero ... de un objeto de clase MyClass a un objeto de clase MyClass !

Para ser un constructor de conversión, el constructor debe tener un solo argumento (en el segundo, el segundo argumento tiene un valor predeterminado) y debe ser declarado sin palabra clave. explicit.

Entonces, la inicialización en main puede verse así:

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") ;
}

Palabra clave explícita y constructores

Ahora, ¿y si hubiéramos usado el explicit palabra clave?

class MyClass
{
  public:
     int a, b;
     explicit MyClass( int i ) {}
}

Entonces, el compilador no aceptaría

   int main()
    {
        MyClass M = 1 ;
    }

ya que esta es una conversión implícita. En cambio, tiene que escribir

   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 La palabra clave siempre se debe usar para evitar la conversión implícita de un constructor y se aplica al constructor en una declaración de clase.


12
2018-02-25 22:09