2012-07-31 12 views
5
struct A { 
    A(int) : i(new int(783)) { 
     std::cout << "a ctor" << std::endl; 
    } 

    A(const A& other) : i(new int(*(other.i))) { 
     std::cout << "a copy ctor" << std::endl; 
    } 

    ~A() { 
     std::cout << "a dtor" << std::endl; 
     delete i; 
    } 

    void get() { 
     std::cout << *i << std::endl; 
    } 

private: 
    int* i; 
}; 

const A& foo() { 
    return A(32); 
} 

const A& foo_2() { 
    return 6; 
} 

int main() 
{ 
    A a = foo(); 
    a.get(); 
} 

Lo sé, devolver las referencias a los valores locales es malo. Pero, por otro lado, la referencia de const debe extender una vida de objeto temporal.¿Qué ocurre exactamente al devolver la referencia const a un objeto local?

Este código produce una salida UB. Entonces no hay extensión de vida.

¿Por qué? Quiero decir, ¿alguien puede explicar qué está pasando paso a paso?

¿Dónde está la falla en mi cadena de razonamiento?

foo():

  1. A (32) - ctor

  2. retorno A (32) - una referencia const a objeto local se crea y se devuelve

  3. A una = foo(); - a se inicializa mediante el valor devuelto foo(), el valor devuelto sale del ámbito (fuera de expresión) y se destruye, pero a ya está inicializado;

(Pero en realidad destructor se llama antes de constructor de copia)

FOO_2():

  1. retorno 6 - temp objeto de tipo A se crea de forma implícita, una referencia constante a este objeto se crea (prolonga su vida útil) y se devuelve

  2. A a = foo(); - a se inicializa mediante el valor devuelto foo(), el valor devuelto sale del ámbito (fuera de expresión) y se destruye, pero a ya está inicializado;

(Pero en realidad destructor se llama antes de constructor de copia)

+1

"la referencia const debe extender la vida útil temporal de un objeto" <- erm, no cuando se trata de las referencias no-const y const de la vida del objeto son iguales y no la extienden * no *. – Giel

+2

Creo que esto es lo que Alexander está hablando en términos de extender la vida útil: http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/ – vmpstr

+1

@Giel: No. La referencia de Const ** puede ** extender la vida útil del objeto temporal. Las referencias de const y non const son bastante diferentes cuando se trata de trabajar con temporarios. En este caso, simplemente funciona de manera diferente a lo que parece esperar OP. – AnT

Respuesta

11

Reglas de extensión de vida temporal para cada contexto específico se extraen explícitamente en la especificación del lenguaje. Y dice que

12.2 Los objetos temporales

5 El segundo contexto es cuando una referencia está vinculado a un temporal. [...] Un límite temporal al valor devuelto en una declaración de devolución de función (6.6.3) persiste hasta que la función finalice. [...]

Su objeto temporal se destruye en el momento de la salida de la función. Eso sucede antes de que comience la inicialización del objeto receptor.

Pareces asumir que tu temporal debería de alguna manera vivir más que eso. Aparentemente estás tratando de aplicar la regla que dice que el temporal debe sobrevivir hasta el final de la expresión completa. Pero esa regla no se aplica a los temporales creados dentro de las funciones. Las vidas de tales temporarios se rigen por sus propias reglas dedicadas.

Tanto su foo como foo_2 producen un comportamiento indefinido, si alguien intenta utilizar la referencia devuelta.

+0

Pero si "un límite temporal al valor devuelto en una declaración de devolución de función (6.6.3) persiste hasta que la función finalice" shoudnt valor temporal se destruirá antes de la inicialización en A foo_3() {return A (54);}? – Alexander

+1

@Alexander - No, 'A foo_3()' devuelve una copia del valor. El valor copiado no se destruye al final de la función. Cuando devuelve una referencia, la referencia también está allí, ya no se refiere a nada. –

+0

Ya veo ... Pero luego copiar ctor debería llamarse 2 veces en la expresión A a = foo_3(); Primera vez cuando se copia el valor de retorno desde el local temporal y la segunda vez al inicializar A a. Pero es llamado solo una vez. ¿O solo es una optimización? – Alexander

3

Está mal interpretando "hasta la salida de la función". Si realmente desea utilizar una referencia constante para extender la vida de un objeto más allá de foo, utilice

A foo() { 
    return A(32); 
} 
int main() { 
    const A& a = foo(); 
} 

Debe volver de foopor valor, y luego usar una referencia constante para hacer referencia al valor de retorno, si desea extender las cosas de la manera que espera.

Como ha dicho @AndreyT, el objeto se destruye en la función que tiene el const &. Desea que el objeto de sobrevivir más allá de foo, y por lo tanto usted debe no tienen const & (o &) en cualquier parte foo o en el tipo de retorno de foo. La primera mención de const & debe estar en main, ya que esa es la función que debe mantener vivo el objeto.

Puede pensar que este código de retorno por valor es lento ya que parece haber copias de A en la devolución, pero esto es incorrecto. En la mayoría de los casos, el compilador puede construir A solo una vez, en su ubicación final (es decir, en la pila de la función de llamada), y luego configurar la referencia relevante.

Cuestiones relacionadas