¿Puede devolver una variable local por valor en C++11/14 dar lugar a que el valor devuelto sea construido por rvalue cuando no hay copia/movimiento involucrado?


Sé que en la siguiente situación que el compilador es libre de mover-construir el valor de retorno de makeA (pero también es libre de elide la copia o mover por completo):

struct A
{
    A(A&);
    A(A&&);
};

A makeA()
{
    A localA;
    return localA;
}

Lo que me pregunto es si al compilador se le permite construir un objeto de tipo A a partir de un objeto local de tipo B por referencia rvalue si se está construyendo en la instrucción return. En otras palabras, en el siguiente ejemplo, se permite al compilador seleccionar el constructor 4 de A para ¿valor devuelto?

struct B { };
struct A {
    A(A&);  // (1)
    A(A&&); // (2)
    A(B&);  // (3)
    A(B&&); // (4)
};

A makeA()
{
    B localB;
    return localB;
}

Pregunto esto porque me parece que la misma lógica que permite que un objeto local de tipo A sea tratado como un rvalue en la sentencia return también debería permitir que un local de cualquier tipo sea tratado como un rvalue, pero no puedo encontrar ejemplos o preguntas de esta naturaleza.

Author: Niall, 2014-09-16

1 answers

La regla para esta situación cambió entre 2011 y 2014. El compilador ahora debería tratar localB como un rvalue.

La regla aplicable para las declaraciones return se encuentra en §12.8 [clase.copy] / p32, que se lee en C++14 (citando N3936, énfasis mío):

Cuando se cumplen los criterios para la elisión de una operación copiar/mover, pero no para una declaración de excepción , y el objeto a copiar es designado por un lvalue, o cuando la expresión en un retorno sentencia es una (posiblemente entre paréntesis) id-expresión que nombra un objeto con duración de almacenamiento automática declarada en el cuerpo o parámetro-declaración-cláusula de la función envolvente más interna o lambda-expresión, la resolución de sobrecarga para seleccionar el constructor para la copia se realiza primero como si el objeto fuera designado por un rvalue. Si la primera resolución de sobrecarga falla o no se realizó, o si el tipo del primer parámetro de la el constructor seleccionado es no es una referencia rvalue al tipo de objeto (posiblemente calificado por cv), resolución de sobrecarga se realiza de nuevo, teniendo en cuenta el objeto como un lvalue.

La cláusula en negrita fue añadida por CWG issue 1579, expresamente para requerir que el constructor de movimiento de conversión A::A(B&&) sea llamado aquí. Esto se implementa en GCC 5 y Clang 3.9.

En 2011, esta regla de "probar rvalue primero" estaba estrechamente vinculada a los criterios para la elisión de copias (citando N3337):

Cuando se cumplen o se cumplirían los criterios para la elisión de una operación de copia met save por el hecho de que el objeto de origen es un parámetro de función, y el objeto a copiar es designado por un lvalue, overload la resolución para seleccionar el constructor para la copia se realiza primero como si el objeto fuera designado por un rvalue.

Dado que copy elision necesariamente requiere que los dos tengan el mismo tipo, este párrafo no se aplicó, y el compilador tuvo que utilice el constructor A::A(B&).

Tenga en cuenta que como CWG 1579 se considera un DR contra C++11, los compiladores deben implementar su resolución incluso en el modo C++11.

 30
Author: T.C.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-17 19:17:32