c++ - utilice - Referencia constante al miembro de objeto temporal
referencias c++ (4)
Como mencioné en mi comentario:
La vida útil del temporizador debe prolongarse siempre que la vida útil de su acceso de miembro (str en este caso). Dicho esto, debería estar bien simplemente tomando el valor de retorno por copia. RVO evitará hacer copias adicionales.
De la norma, sección 12.2.5:
El segundo contexto es cuando una referencia está vinculada a un temporal. El temporal al que se enlaza la referencia o el temporal que es el objeto completo de un subobjeto al que se enlaza la referencia persiste durante toda la vida útil de la referencia, excepto:
- Un enlace temporal a un miembro de referencia en el inicializador ctor de un constructor (12.6.2) persiste hasta que el constructor sale.
- Un enlace temporal a un parámetro de referencia en una llamada de función (5.2.2) persiste hasta la finalización de la expresión completa que contiene la llamada.
Para evitar cualquier problema, prefiero hacer:
auto m_p = getPair(x);
Esto es lo más eficiente que puede obtener debido a RVO
que cada compilador debe estar haciendo en este caso.
Esta pregunta ya tiene una respuesta aquí:
Es una característica conocida de C ++ que una referencia constante prolonga la vida útil del objeto temporal devuelto desde una función, pero ¿es aceptable usar una referencia constante al miembro del objeto temporal devuelto desde la función?
Ejemplo:
#include <string>
std::pair<std::string, int> getPair(int n)
{
return {std::to_string(n), n};
}
int main(int, char*[])
{
const int x = 123456;
const auto& str = getPair(x).first;
printf("%d = %s/n", x, str.c_str());
return 0;
}
Salida:
123456 = 123456
Está bien definido.
Desde el estándar: $ 12.2 / 6 objetos temporales [class.temporary] :
(énfasis mío)
El temporal al que se vincula la referencia o el temporal que es el objeto completo de un subobjeto al que se enlaza la referencia persiste durante toda la vida útil de la referencia
Y sobre el subobjeto , $ 1.8 / 2 El modelo de objeto C ++ [intro.object] :
(énfasis mío)
Los objetos pueden contener otros objetos, llamados subobjetos. Un subobjeto puede ser un subobjeto miembro ([class.mem]), un subobjeto de clase base (Cláusula [class.derived]), o un elemento de matriz. Un objeto que no es un subobjeto de ningún otro objeto se llama un objeto completo .
first
está obligado a hacer referencia y es el subobjeto miembro de std::pair
, por lo que la vida útil temporal de std::pair
(es decir, el objeto completo) se prolongará, el código debería estar bien.
Solo para referencia: Clang y GCC dicen que sí, VC dice que no.
Esto parece abordado en 12.2 / 4-5:
Hay dos contextos en los que los temporales se destruyen en un punto diferente al final de la expresión completa. El primer contexto ... [cosas que tratan con matrices]
El segundo contexto es cuando una referencia está vinculada a un temporal. El temporal al que se enlaza la referencia o el temporal que es el objeto completo de un subobjeto al que se enlaza la referencia persiste durante toda la vida útil de la referencia, excepto:
Hay cuatro excepciones relacionadas con el enlace del constructor de los miembros de referencia, las llamadas de función, las funciones que se devuelven por referencia y el enlace de referencias en los nuevos inicializadores.
Ninguno de estos casos se aplica, por lo que el temporal se destruye al final de la declaración completa, dejando una referencia a un miembro del temporal.
Simplemente ayude al compilador a darse cuenta de que puede moverse desde lo temporal: const auto str = std::move(getPair(x).first);
Sí, este código es perfectamente aceptable. Las reglas, según el estándar son ([class.temporary]):
Hay dos contextos en los que los temporales se destruyen en un punto diferente al final de la fullexpresión. El primer contexto es cuando se llama a un constructor predeterminado para inicializar un elemento de una matriz. Si el constructor tiene uno o más argumentos predeterminados, la destrucción de cada temporal creado en un argumento predeterminado se secuencia antes de la construcción del siguiente elemento de la matriz, si corresponde.
El segundo contexto es cuando una referencia está vinculada a un temporal. El temporal al que está vinculada la referencia o el temporal que es el objeto completo de un subobjeto al que la referencia está vinculada persiste durante el tiempo de vida de la referencia ...
Como puede ver, la línea resaltada deja en claro que la referencia de enlace a subobjetos es aceptable, ya que el objeto completo también debe tener su vida útil extendida.
Tenga en cuenta que first
califica como un subobjeto [intro.object]:
- Los objetos pueden contener otros objetos, llamados subobjetos. Un subobjeto puede ser un subobjeto miembro (9.2), un subobjeto de clase base (Cláusula 10) o un elemento de matriz. Un objeto que no es un subobjeto de ningún otro objeto se llama un objeto completo.