es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

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.

Tags:  , ,

Answer

  1. Avatar for davy.ai

    El constructor de movimiento se llama en create2 porque la declaración if con la condición constexpr siempre es verdadera, lo que significa que el tipo de retorno de la función es Person, no Person&&. 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 a p2.

    En create1, el valor de retorno ya es un rvalue porque es el resultado de la expresión return p1;, donde p1 es una variable local de create1. De manera similar, en create3, la expresión return 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.

Comments are closed.