Elisión de copia y constructor de movimiento
Considere la siguiente definición de Person
:
struct Person
{
Person() { std::cout << "construct, "; }
Person(const Person&) { std::cout << "copy\n"; }
Person(Person&&) { std::cout << "move\n"; }
};
Y tres funciones diferentes para crear un Person
:
Person create1()
{
std::cout << "create1: ";
Person p1{};
return p1;
}
Person create2()
{
std::cout << "create2: ";
if constexpr (true)
{
Person p1{};
return p1;
}
else
{
Person p2{};
return p2;
}
}
Person create3()
{
std::cout << "create3: ";
if constexpr (true)
{
return Person{};
}
else
{
return Person{};
}
}
Finalmente, llamo a las funciones create de la siguiente manera:
int main()
{
Person p1 = create1();
Person p2 = create2();
Person p3 = create3();
return 0;
}
El resultado es:
create1: construct
create2: construct, move
create3: construct
Lo que me molesta es la salida de create2
. Si en create1
y create3
, el constructor de movimiento no se llama, ¿por qué se llama en create2
?
Estoy utilizando GCC 12.0.0.
EDITAR: Clang 13.0.0 no llama al constructor de movimiento.
davy.ai
El constructor de movimiento se llama en
create2
porque la declaraciónif
con la condiciónconstexpr
siempre es verdadera, lo que significa que el tipo de retorno de la función esPerson
, noPerson&&
. Esto significa que el valor de retorno es un lvalue y, por lo tanto, se invoca el constructor de movimiento para convertirlo en un rvalue para la asignación ap2
.En
create1
, el valor de retorno ya es un rvalue porque es el resultado de la expresiónreturn p1;
, dondep1
es una variable local decreate1
. De manera similar, encreate3
, la expresiónreturn Person{};
crea un objeto temporal, que ya es un rvalue, por lo que no es necesario realizar el movimiento.Vale la pena señalar que en la práctica, el compilador puede omitir el constructor de movimiento en casos como
create2
, ya que el valor de retorno es un objeto con nombre que no tiene otras aplicaciones en la función. Esto se conoce como “Optimización del Valor de Retorno” (RVO) y permite al compilador evitar copias y movimientos costosos en ciertas situaciones.