c++ - significado - Copia elision al crear un objeto dentro de emplace()
que es emplazar diccionario juridico (4)
Veo un montón de código en el trabajo donde las personas usan emplace y emplace_back con un objeto temporal, como este:
struct A {
A::A(int, int);
};
vector<A> v;
vector<A>.emplace_back(A(1, 2));
Sé que todo el punto de emplace_back es poder pasar los parámetros directamente, como esto:
v.emplace_back(1, 2);
Pero desafortunadamente esto no está claro para algunas personas. Pero no nos detengamos en eso ...
Mi pregunta es: ¿es el compilador capaz de optimizar esto y omitir crear y copiar? ¿O debería realmente tratar de arreglar estas ocurrencias?
Para su referencia ... estamos trabajando con C ++ 14.
¿Es el compilador capaz de optimizar esto y omitir crear y copiar?
No hay necesariamente una copia involucrada. Si hay un constructor de movimiento disponible, habrá un movimiento. Esto no se puede optimizar, ya que el caso de inicialización directa solo llamará al constructor init, mientras que en el otro caso, el constructor de movimiento se llamará adicionalmente (incluidos sus efectos secundarios).
Por lo tanto, si es posible, debe refactorizar esos códigos.
Mi pregunta es: ¿es el compilador capaz de optimizar esto y omitir crear y copiar? ¿O debería realmente tratar de arreglar estas ocurrencias?
No se puede evitar una copia, en el caso general. Dado que emplace_back
acepta reenviar referencias, debe crear temporales desde una perspectiva pura y normal. Esas referencias deben unirse a los objetos, después de todo.
Copy elision es un conjunto de reglas que permite evitar un constructor de copia (o movimiento), y una copia eliminada, incluso si el constructor y el destructor correspondiente tienen efectos secundarios. Se aplica sólo en circunstancias específicas. Y pasar argumentos por referencia no es uno de esos. Por lo tanto, para los tipos no triviales, donde las copias de objetos no pueden ser alineadas por la regla de "si", las manos del compilador están atadas si su objetivo es ser conforme a la norma.
La respuesta fácil es no; Elision no funciona con un reenvío perfecto. Pero esto es c ++, entonces la respuesta es sí.
Requiere un toque de boilerplate:
struct A {
A(int, int){std::cout << "A(int,int)/n"; }
A(A&&){std::cout<<"A(A&&)/n";}
};
template<class F>
struct maker_t {
F f;
template<class T>
operator T()&&{ return f(); }
};
template<class F>
maker_t<std::decay_t<F>> maker( F&& f ) { return {std::forward<F>(f)}; }
vector<A> v;
v.emplace_back(maker([]{ return A(1,2); }));
La salida es una llamada a A(int,int)
. No se produce movimiento. En c ++ 17, la creación ni siquiera requiere que exista un constructor de movimientos (pero sí el vector, ya que cree que puede tener que mover los elementos en un búfer ya asignado). En c ++ 14 los movimientos son simplemente elididos.
Solo quiero añadir
Hay una gran charla de 5 minutos sobre elección de copias y RVO de Jon Kalb https://youtu.be/fSB57PiXpRw
Además, puede obtener diferentes resultados utilizando diferentes compiladores gcc, clang o icc
Vea el explorador de compiladores , pruebe diferentes compiladores y configuraciones y vea por sí mismo