c++ variables - Devolver la referencia constante a la variable local de una función




locales ejemplos (5)

Tengo algunas preguntas sobre cómo devolver una referencia a una variable local desde una función:

class A
{
public:
        A(int xx):x(xx)
 {
  printf("A::A()\n");
 }
};

const A& getA1()
{
 A a(5);
 return a;
}

A& getA2()
{
 A a(5);
 return a;
}

A getA3()
{
 A a(5);
 return a;
}

int main()
{ 
     const A& newA1 = getA1(); //1
     A& newA2 = getA2();       //2
     A& newA3 = getA3();       //3
}

Mis preguntas son =>

  1. ¿Es correcta la implementación de getA1() ? Siento que es incorrecto ya que devuelve la dirección de una variable local o temporal.

  2. ¿Cuál de las afirmaciones en main (1,2,3) conducirá a un comportamiento indefinido?

  3. En const A& newA1 = getA1(); ¿El estándar garantiza que un límite temporal por una referencia constante no será destruido hasta que la referencia salga del alcance?


Answers

Creo que el problema principal es que no regresas temporalmente, deberías

return A(5);

más bien que

A a(5);
return a;

De lo contrario, devolverá una dirección de variable local, no temporal. Y la referencia temporal a const solo funciona para temporarios.

Creo que se explica aquí: herbsutter.wordpress.com/2008/01/01/…


1. ¿Es correcta la implementación getA1() ? Siento que es incorrecta, ya que es la dirección de devolución de la variable local o temporal.

La única versión de getAx() que es correcta en su programa es getA3() . Los otros tienen un comportamiento indefinido sin importar cómo los use más adelante.

2. ¿Cuál de las declaraciones en la parte principal (1,2,3) conducirá a un comportamiento indefinido?

En un sentido ninguno de ellos. Para 1 y 2, el comportamiento indefinido es como resultado de los cuerpos de las funciones. Para la última línea, newA3 debería ser un error de compilación ya que no puede vincular una referencia temporal a una referencia no const.

3. En const A& newA1 = getA1(); ¿las garantías estándar que el límite temporal por una referencia const no se destruirá hasta que la referencia salga del alcance?

No. El siguiente es un ejemplo de eso:

A const & newConstA3 = getA3 ();

Aquí, getA3() devuelve un temporal y la duración de ese temporal ahora está vinculada al objeto newConstA3 . En otras palabras, el temporal existirá hasta que newConstA3 fuera del alcance.


Q1: Sí, esto es un problema, vea la respuesta a Q2.

Q2: 1 y 2 no están definidos ya que se refieren a variables locales en la pila de getA1 y getA2. Esas variables están fuera del alcance y ya no están disponibles y se puede sobrescribir lo peor, ya que la pila cambia constantemente. getA3 funciona ya que se crea una copia del valor de retorno y se devuelve a la persona que llama.

P3: No existe tal garantía para ver la respuesta a Q2.


Si compila esto en VC6 obtendrá esta advertencia

****** Advertencia del compilador (nivel 1) C4172 dirección de retorno de la variable local o temporal Una función devuelve la dirección de una variable local o un objeto temporal. Las variables locales y los objetos temporales se destruyen cuando se devuelve una función, por lo que la dirección devuelta no es válida. ******

Mientras probaba este problema encontré algo interesante (dado que el código está funcionando en VC6):

 class MyClass
{
 public:
 MyClass()
 {
  objID=++cntr;
 }
MyClass& myFunc()
{
    MyClass obj;
    return obj;
}
 int objID;
 static int cntr;
};

int MyClass::cntr;

main()
{
 MyClass tseadf;
 cout<<(tseadf.myFunc()).objID<<endl;

}

No. Siempre que una variable local en una declaración de return es elegible para la elección de copia, se enlaza a una referencia de rvalor y, por lo tanto, return t; es idéntico a return std::move(t); en su ejemplo con respecto a qué constructores son elegibles.

Tenga en cuenta sin embargo que return std::move(t); evita que el compilador ejerza elision de copia, mientras return t ; No lo hace, y por lo tanto este último es el estilo preferido. [Gracias a @Johannes por la corrección.] Si ocurre una copia de la copia, la cuestión de si se usa o no la construcción de movimientos se convierte en un punto discutible.

Ver 12.8 (31, 32) en la norma.

Tenga en cuenta también que si T tiene una copia accesible, pero un constructor de movimientos eliminado, return t; no compilará, porque el constructor de movimientos debe considerarse primero; tendrías que decir algo al efecto de return static_cast<T&>(t); para que funcione:

T f()
{
    T t;
    return t;                 // most likely elided entirely
    return std::move(t);      // uses T::T(T &&) if defined; error if deleted or inaccessible
    return static_cast<T&>(t) // uses T::T(T const &)
}




c++ reference undefined const-reference