Estoy creando una clase de tipo encadenamiento, como el pequeño ejemplo a continuación. Parece que al encadenar funciones miembro, se invoca el constructor de copia. ¿Hay alguna forma de deshacerse de la llamada al constructor de la copia? En mi ejemplo de juguete a continuación, es obvio que solo estoy tratando con temporales y, por lo tanto, "debería" (tal vez no según los estándares, pero lógicamente) ser una elisión. La segunda mejor opción, para copiar elisión, sería llamar al constructor del movimiento, pero este no es el caso.
class test_class {
private:
int i = 5;
public:
test_class(int i) : i(i) {}
test_class(const test_class& t) {
i = t.i;
std::cout << "Copy constructor"<< std::endl;
}
test_class(test_class&& t) {
i = t.i;
std::cout << "Move constructor"<< std::endl;
}
auto& increment(){
i++;
return *this;
}
};
int main()
{
//test_class a{7};
//does not call copy constructor
auto b = test_class{7};
//calls copy constructor
auto b2 = test_class{7}.increment();
return 0;
}
Editar: algunas aclaraciones. 1. Esto no depende del nivel de optimización. 2. En mi código real, tengo objetos más complejos (p. Ej., Montón asignado) que ints
auto b = test_class{7};
no llama al constructor de copias porque es realmente equivalentetest_class b{7};
y los compiladores son lo suficientemente inteligentes como para reconocer este caso y, por lo tanto, pueden eludir fácilmente cualquier copia. No se puede hacer lo mismob2
.std::cout
) en su copiadora? Sin ella, la copia debería optimizarse.Respuestas:
Respuesta parcial (no se construye
b2
en su lugar, pero convierte la construcción de la copia en una construcción de movimiento): puede sobrecargar laincrement
función miembro en la categoría de valor de la instancia asociada:Esto causa
mover-construir
b2
porquetest_class{7}
es temporal, y se llama a la&&
sobrecarga detest_class::increment
.Para una verdadera construcción en el lugar (es decir, ni siquiera una construcción de movimiento), puede convertir todas las funciones de miembros especiales y no especiales en
constexpr
versiones. Entonces puedes hacery no tiene que pagar una mudanza ni una copia de construcción. Obviamente, esto es posible para el
test_class
escenario simple , pero no para un escenario más general que no permitaconstexpr
funciones miembro.fuente
b2
no sea modificable.constinit
que sería el camino a seguir allí.this
mismo que lasconst
funciones de los miembrosBásicamente, asignar una referencia a un valor requiere invocar un constructor, es decir, una copia o un movimiento . Esto es diferente de la copia-elisión, donde se sabe que en ambos lados de la función es el mismo objeto distinto. También una referencia puede referirse a un objeto compartido como un puntero.
Probablemente, la forma más simple es hacer que el constructor de copias esté completamente optimizado. El valor ya está optimizado por el compilador, es solo el
std::cout
que no se puede optimizar.(o simplemente elimine la copia y mueva el constructor)
ejemplo en vivo
Dado que su problema es básicamente con la referencia, una solución probablemente no sea devolver una referencia al objeto si desea dejar de copiar de esta manera.
Un tercer método es confiar en la elisión de copia en primer lugar; sin embargo, esto requiere una reescritura o encapsulación de la operación en una función y, por lo tanto, evitar el problema por completo (soy consciente de que esto puede no ser lo que desea, pero podría ser un solución a otros usuarios):
Un cuarto método está utilizando un movimiento en su lugar, ya sea invocado explícitamente
o como se ve en esta respuesta .
fuente