¿C ++ 17 prohíbe la elisión de copia en un caso donde C ++ 14 lo permitió?
Considera lo siguiente:
struct X {
X() {}
X(X&&) { puts("move"); }
};
X x = X();
En C ++ 14, el movimiento podría eludirse a pesar de que el constructor del movimiento tiene efectos secundarios gracias a [class.copy] / 31,
Esta elisión de operaciones de copiar / mover ... está permitida en las siguientes circunstancias ... cuando un objeto de clase temporal que no se ha vinculado a una referencia (12.2) se copiará / moverá a un objeto de clase con el mismo cv-no calificado tipo
En C ++ 17 esta bala fue eliminada. En cambio, se garantiza que el movimiento se eliminará gracias a [dcl.init] /17.6.1:
Si la expresión inicializadora es un valor prva y la versión no calificada por cv del tipo fuente es la misma clase que la clase del destino, la expresión inicializadora se usa para inicializar el objeto de destino. [Ejemplo: T x = T(T(T()));
llama alT
constructor predeterminado para inicializarx
. -ejemplo final ]
Hasta ahora, los hechos que he declarado son bien conocidos. Pero ahora cambiemos el código para que lea:
X x({});
En C ++ 14, se realiza la resolución de sobrecarga y{}
se convierte a un tipo temporalX
utilizando el constructor predeterminado, luego se trasladó ax
. Las reglas de copia de elisión permiten eludir este movimiento.
En C ++ 17, la resolución de sobrecarga es la misma, pero ahora [dcl.init] /17.6.1 no se aplica y la viñeta de C ++ 14 ya no está allí. No hay una expresión de inicializador, ya que el inicializador es una lista de inicialización arriostrada. En cambio, parece que se aplica [dcl.init] / (17.6.2):
De lo contrario, si la inicialización es inicialización directa, o si es una inicialización de copia donde la versión no calificada por cv del tipo de origen es la misma clase o una clase derivada de la clase del destino, se consideran los constructores. Los constructores aplicables se enumeran (16.3.1.3), y el mejor se elige mediante la resolución de sobrecarga (16.3). El constructor así seleccionado se llama para inicializar el objeto, con la expresión inicializadora o la lista de expresiones como argumento (s). Si no se aplica ningún constructor, o la resolución de sobrecarga es ambigua, la inicialización está mal formada.
Esto parece requerir que se llame al constructor de movimiento, y si hay una regla en otra parte del estándar que dice que está bien eludirlo, no sé dónde está.