¿Cuál es la diferencia entre el alloctaor :: construct más antiguo y el constructor nuevo y explícito?

15

Como sé std::allocator<T>::construct, solo toma dos parámetros en la versión anterior de C ++; el primero es un puntero a la memoria sin construir, en bruto, en la que queremos construir un objeto de tipo Ty el segundo es un valor de tipo de elemento para inicializar ese objeto. Entonces se invoca el constructor de copia:

struct Foo {
    Foo(int, int) { cout << "Foo(int, int)" << endl; }
    /*explicit*/ Foo(int) { cout << "Foo(int)" << endl; }
    Foo(const Foo&) { cout << "Foo(const Foo&)" << endl; }
};

int main(int argc, char* argv[]) {


    allocator<Foo> a;
    Foo* const p = a.allocate(200, NULL); // second parameter is required on C++98 but on C++11 it is optional
//  Foo* const p = a.allocate(200); // works fine on C++11 but not on C++98

    a.construct(p, 5, 7); // works on C++ 11 and up but not C++98
    a.construct(p, 10);// works on both
    a.destroy(p);
    a.destroy(p + 1);
    a.deallocate(p, 200);



    std::cout << std::endl;
}
  • ¿Por qué en C ++ 98 a.construct(p, 10)llamando al constructor de copia pero en C ++ 11 y superior está llamando solo al constructor que toma un entero?

  • ¿Quiere decir esto en C ++ 11 debido a una optimización de copia-elisión incluso si el constructor Foo(int)es explicitobras en dicha llamada: a.construct(p, 5)obras en C ++ 11 incluso el constructor es explicitlo que estoy seguro es de que no funciona en C ++ 98 si Foo(int)es explicit.

  • Si es así, si compilo esa declaración con algún tipo de copy-elisionoptimización de desactivación, ¿ hará que falle el compilador? Gracias.

Itachi Uchiwa
fuente
3
Respuesta corta: hasta C ++ 11, no hubo una perfecta aceptación . Detalles proporcionados a continuación por @flyx. Tenga en cuenta que no hay una elisión de copia involucrada (sin pasar por valor o retorno por valor).
Daniel Langr el

Respuestas:

13

Esto se debe a la declaración de construct cambio en C ++ 11 :

void construct( pointer p, const_reference val );  (1)  (until C++11)
template< class U, class... Args >
void construct( U* p, Args&&... args );            (2)  (since C++11)

La primera declaración llama al constructor de copia, mientras que la segunda llama a un constructor que coincide con la lista de argumentos dada. Este podría ser el constructor de copia, pero también otro constructor como viste en tu código.

a.construct(p, 10)llama al constructor de copia en C ++ 98 porque 10se convierte implícitamente a Footravés del Foo(int)constructor. Esta conversión no es necesaria en C ++ 11 ya que existe un constructor coincidente que toma un int(exactamente el constructor que se utilizó para convertir en C ++ 98). Esta es también la razón por la cual el código no funciona en C ++ 98 cuando agrega explicit, no puede convertirlo 10en un Fooarchivo.

flyx
fuente