2009-07-28 28 views
7

Tengo una función que devuelve un objeto de tipo Foo:Asignación resultado de la función que devuelve un Foo a una constante Foo y

Foo getFoo(); 

Sé que la siguiente se compilará y va a funcionar, pero ¿por qué habría de hacerlo alguna vez hacerlo?

const Foo& myFoo = getFoo(); 

Para mí, la siguiente es mucho más fácil de leer, y no me obliga a recordar que C++ me permite asignar un valor R a una referencia constante:

const Foo myFoo = getFoo(); 

¿Cuáles son las diferencias entre los dos? ¿Por qué usaría el primero sobre el segundo? ¿Por qué debería usar el segundo sobre el primero?

Respuesta

4

Contrariamente a la opinión popular, no hay garantía de que el asignar el resultado de una función que devuelve un objeto de valor a una referencia constante se traducirá en un menor número de copias de asignar al objeto sí mismo.

Cuando asigna un valor de referencia a una referencia constante, el compilador puede enlazar la referencia de dos formas. Puede crear un nuevo temporal al copiar el valor r y enlazar la referencia a eso, o puede enlazar la referencia directamente con el valor r mismo.

Si el compilador no puede hacer la optimización 'obvia' para eliminar el temporal y elide el constructor de copia para el valor de retorno de getFoo, entonces ¿qué tan probable es que sea capaz de hacer la forma más eficiente de vincular una rvalue a una referencia constante sin hacer un nuevo temporal?

Una razón para usar una referencia de referencia sería hacer que la función sea más robusta frente a posibles cortes.Si el tipo de retorno fuera en realidad un tipo derivado de Foo, la asignación a una referencia de clase de clase base estaría garantizada para no cortar, incluso si el compilador creó un objeto temporal a partir del valor r devuelto por la función. El compilador también generará la llamada correcta al destructor de clase derivado independientemente de si el destructor en la clase base es virtual o no. Esto se debe a que el tipo de objeto temporal creado se basa en el tipo de expresión que se está asignando y no en el tipo de la referencia que se está inicializando.

nota que se hizo la cuestión de cómo muchas copias del valor de retorno es totalmente independiente de la optimización valor de retorno y la optimización valor de retorno llamado. Estas optimizaciones se refieren a eliminar la copia del resultado rvalue de evaluar una expresión de retorno o de una variable local con nombre en el valor de retorno de una función en el cuerpo de la función en sí. Obviamente, en el mejor de los casos posibles, se puede realizar una optimización del valor de retorno y se puede eliminar el valor de retorno temporal para que no se realice ninguna copia en el objeto devuelto.

+0

Si el compilador vincula un valor r para const referencia haciendo una copia, que puede implicar un constructor de copia, que está definido por A :: A (const A &), ¿cómo compilaría el siguiente código? A x = A (param); –

+0

@ZheYang: a partir de una nota en el antiguo estándar: "Claramente, si la inicialización de referencia que se procesa es una para el primer argumento de una llamada de constructor de copia, una implementación debe elegir la primera alternativa (vincular sin copiar) para evitar la recursión infinita ". En el nuevo estándar, esta opción se ha eliminado de las implementaciones. –

+0

Interesante. Creo que es solo porque en el nuevo estándar ahora tenemos el constructor de movimientos, pero de todos modos puede faltar. –

1

Es válido para permitir este tipo de patrón:

void foo(const SomeType &); 

foo(SomeType(bar)) 

En este caso, un SomeType temporal se construye y se pasó a foo. Lo más probable es que el hecho de que también pueda tener referencias constantes en la pila a los temporales sea un efecto secundario de la verborrea utilizada para definir este comportamiento en el estándar. Tenga en cuenta que, como onebyone mencionado en los comentarios, la vida útil temporal se extiende a la de la referencia en sí.

+0

Así que no hay buenas razones para hacer 'const Foo & myFoo = getFoo()'? –

+0

¿Qué pregunta está tratando de responder? –

+0

"también puede tener referencias a temporarios que sobreviven a los temporales mismos". No así, no puedes. En la segunda línea de código de la pregunta, el temporal devuelto por getFoo() no se destruye hasta que 'myFoo' queda fuera del alcance. http://herbsutter.wordpress.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/ –

0

Podría haber varias razones:

lo que si usted no desea que un objeto constante refiere? Por ejemplo, esto no funcionará:

 
    const Foo &myFoo = getFoo(); 
    myFoo.myfield = x; 

O, lo que si usted está volviendo un objeto de temperatura getFoo()? Esto le advierten sobre el retorno de una referencia (o dirección) a un local de:

 
    const Foo &getFoo(void) 
    { 
     Foo localFoo(); 
     // do the things you want to localFoo 
     return(localFoo); 
    } 

las partes internas de const Foo& myFoo = getFoo() hacen más o menos lo mismo que Foo myFoo = getFoo() por lo que el argumento de que hay un valor de rendimiento para el retorno ref const no son válidos . No encuentro problema para devolver objetos de tamaño razonable.

Descargo de responsabilidad - No probé estos ejemplos en gcc. Su kilometraje puede variar en consecuencia.

+0

El hecho de que no puedes hacer cosas sin const en 'myFoo 'aplica si se almacena el valor de retorno de' getFoo() 'en un' const Foo & 'o un' const Foo'. Además, estoy hablando específicamente de 'getFoo()' devolviendo 'const Foo' en lugar de' const Foo & '. –

Cuestiones relacionadas