c++ - tenga - elisión de copia: el constructor de movimiento no se llama cuando se usa la expresión ternaria en la declaración de devolución?
forma ternaria ejemplos (1)
La especificación para el operador condicional es tan complicada que da miedo. Pero creo que su compilador es correcto en su comportamiento. Ver 5.16 [expr.cond] / p4:
Si el segundo y tercer operandos son glvalues de la misma categoría de valor y tienen el mismo tipo, el resultado es de ese tipo y categoría de valor ...
También vea 12.8 [class.copy], p31, b1 que describe cuándo está permitida la elisión de copia:
en una instrucción de
return
en una función con un tipo de retorno de clase, cuando la expresión es el nombre de un objeto automático no volátil (que no sea una función o parámetro catch-clause) con el mismo tipo cv-no calificado que el tipo de retorno de función, la operación de copiar / mover se puede omitir ...
La expresión no es el nombre de un objeto automático, sino que es una expresión condicional (y esa expresión condicional es un valor l). Así que copiar elisión no está permitido aquí, y no hay nada más que diga que la expresión lvalue puede pretender ser un valor r para la resolución de sobrecarga.
Considere el siguiente ejemplo:
#include <cstdio>
class object
{
public:
object()
{
printf("constructor/n");
}
object(const object &)
{
printf("copy constructor/n");
}
object(object &&)
{
printf("move constructor/n");
}
};
static object create_object()
{
object a;
object b;
volatile int i = 1;
// With #if 0, object''s copy constructor is called; otherwise, its move constructor.
#if 0
if (i)
return b; // moves because of the copy elision rules
else
return a; // moves because of the copy elision rules
#else
// Seems equivalent to the above, but behaves differently.
return i ? b : a; // copies (with g++ 4.7)
#endif
}
int main()
{
auto data(create_object());
return 0;
}
Y considere este fragmento del C ++ 11 Working Draft, n3337.pdf, 12.8 [class.copy], punto 32:
Cuando se cumplen o se cumplirían los criterios para elisión de una operación de copia, salvo por el hecho de que el objeto fuente es un parámetro de función, y el objeto que se va a copiar está designado por un lvalue, la resolución de sobrecarga para seleccionar el constructor para la copia es primero se realizó como si el objeto fuera designado por un valor r . Si la resolución de sobrecarga falla, o si el tipo del primer parámetro del constructor seleccionado no es una referencia rvalue al tipo del objeto (posiblemente cv-qualified), la resolución de sobrecarga se realiza nuevamente, considerando el objeto como un valor l. [Nota: esta resolución de sobrecarga de dos etapas se debe realizar independientemente de si se producirá elisión de copia. Determina el nombre del constructor que se invocará si no se realiza una elisión, y el constructor seleccionado debe estar accesible incluso si se elimina la llamada. -finalizar nota]
Por lo tanto, si utilizamos #if 1
en el ejemplo, el constructor de movimientos se prueba primero al devolver el objeto y luego el constructor de copias. Como tenemos un constructor de movimientos, se usa en lugar del constructor de copias.
Sin embargo, en la última instrucción return
en create_object()
, hemos observado que el constructor de movimientos no se usa. ¿Es o no es esto una violación de las reglas del lenguaje? ¿El lenguaje requiere que el constructor de movimiento se use en la última declaración de return
?