2011-12-17 23 views
51

Cuando uso C++ 11 auto, ¿cuáles son las reglas de tipo de deducción con respecto a si se resolverá con un valor o una referencia?C++ 11 semántica "automática"

por ejemplo, a veces es clara:

auto i = v.begin(); // Copy, begin() returns an iterator by value 

Estos son menos claras:

const std::shared_ptr<Foo>& get_foo(); 
auto p = get_foo(); // Copy or reference? 

static std::shared_ptr<Foo> s_foo; 
auto sp = s_foo; // Copy or reference? 

std::vector<std::shared_ptr<Foo>> c; 
for (auto foo: c) { // Copy for every loop iteration? 

Respuesta

58

La regla es simple: es la forma en que se declara.

int i = 5; 
auto a1 = i; // value 
auto & a2 = i; // reference 

Siguiente ejemplo demuestra que:

#include <typeinfo> 
#include <iostream>  

template< typename T > 
struct A 
{ 
    static void foo(){ std::cout<< "value" << std::endl; } 
}; 
template< typename T > 
struct A< T&> 
{ 
    static void foo(){ std::cout<< "reference" << std::endl; } 
}; 

float& bar() 
{ 
    static float t=5.5; 
    return t; 
} 

int main() 
{ 
    int i = 5; 
    int &r = i; 

    auto a1 = i; 
    auto a2 = r; 
    auto a3 = bar(); 

    A<decltype(i)>::foo();  // value 
    A<decltype(r)>::foo();  // reference 
    A<decltype(a1)>::foo();  // value 
    A<decltype(a2)>::foo();  // value 
    A<decltype(bar())>::foo(); // reference 
    A<decltype(a3)>::foo();  // value 
} 

La salida:

value 
reference 
value 
value 
reference 
value 
+5

Tenga en cuenta que el '// puntero' no es realmente necesario. Además, el hecho de que un compilador proporcione esta salida no significa que se ajuste de manera estándar. ;) En este caso, es correcto, aunque se puede dar una mejor explicación de lo que sucede exactamente (el tipo deducido está "decaído"). – Xeo

12

§7.1.6.4 [dcl.spec.auto] p6

Una vez que el tipo de un declarador-id ha sido determinar d de acuerdo con 8.3, el tipo de la variable declarada que utiliza el declarator-id se determina a partir del tipo de su inicializador usando las reglas para la deducción del argumento de la plantilla.

Esto significa nada más que auto modelos de argumento de deducción de la plantilla durante una llamada a la función.

template<class T> 
void f(T){} // #1, will also be by-value 

template<class T> 
void g(T&){} // #2, will always be by-reference 

Nota # 1 que siempre habrá copiar el argumento pasado, no importa si se pasa una referencia o cualquier otra cosa. (A menos que especifique específicamente el argumento de la plantilla como f<int&>(intref);.)

+0

Entonces, ¿qué significa esto exactamente para bucles for basados ​​en rangos? Pensé que significaba que eran referencias (lo que me parecería lógico), pero descubrí que esto no sucedió en un caso. – leftaroundabout

+3

@leftaroundabout: Eso no es lógico. El 'auto' funciona igual. 'for (auto val: range)' siempre copiará, 'for (auto & ref: range)' siempre será una referencia. Y para confundir aún más 'for (auto && x: range)' será 'T &&' o 'T &' dependiendo de si '* begin (range)' devolverá un valor o una referencia. – Xeo

9

Lo que obtenga del lado derecho (de "=") nunca es una referencia. Más específicamente, el resultado de una expresión nunca es una referencia. En esta luz, observe la diferencia entre los resultados en el ejemplo.

#include <typeinfo> 
#include <iostream> 

template< typename T > 
struct A 
{ 
    static void foo(){ std::cout<< "value" << std::endl; } 
}; 

template< typename T > 
struct A< T&> 
{ 
    static void foo(){ std::cout<< "reference" << std::endl; } 
}; 

float& bar() 
{ 
    static float t=5.5; 
    return t; 
} 

int main() 
{ 
    auto a3 = bar(); 

    A<decltype(bar())>::foo(); // reference 
    A<decltype(a3)>::foo(); // value 
} 
+0

¡Incluya el resto del ejemplo! Esta es la respuesta más sucinta, pero necesita leer otra para entenderla ... – povman

+0

Esa primera oración es precisamente lo que estaba buscando. Gracias. –