C++11中的"auto"语义学

83

当我使用C++11中的auto时,关于它是解析为值还是引用的类型推导规则是什么?

例如,有时很清楚:

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

这些表述不够清晰:

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?
3个回答

98
规则很简单:它取决于你如何声明它。
int i = 5;
auto a1 = i;    // value
auto & a2 = i;  // reference

下一个例子证明了这一点:
#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
}

输出结果:
value
reference
value
value
reference
value

6
请注意,// pointer并不是必需的。而且,只因为一个编译器给出了这个输出,并不意味着它符合标准。;) 在这种情况下,虽然是正确的,但更好的解释是什么确切地发生了(推断类型被“衰减”了)。 - Xeo
@Nikos 多年后重新审视这个问题(和答案)...结果发现确实存在类型可能会变化的情况:通用引用。基本上,像 auto&& a = ...T&& b = ...(在模板中)这样的声明将根据表达式的求值结果是左值还是右值而确定其左值或右值。 - Alex B

15

§7.1.6.4 [dcl.spec.auto] p6

一旦根据8.3确定了declarator-id的类型,使用该declarator-id声明的变量的类型将使用模板参数推导规则从其初始化器的类型中确定。

这意味着auto在函数调用期间模拟了模板参数推导。

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

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

注意,无论您传递引用或其他任何内容,#1 都将始终复制传递的参数。 (除非您明确指定模板参数,例如f<int&>(intref);。)

那么这对基于范围的for循环到底意味着什么?我原以为它意味着它们是按引用传递的(这对我来说似乎是合理的),但刚刚发现在某种情况下并非如此。 - leftaroundabout
3
@leftaroundabout: 这不合逻辑。auto 的使用方式是一样的。 for(auto val : range) 会始终复制,for(auto& ref : range) 将始终是一个引用。更令人困惑的是,for(auto&& x : range) 可能是 T&&T&,具体取决于 *begin(range) 是否返回一个值或引用。 - Xeo

11

无论你从“=”号的右侧得到什么,它都不是一个引用。更具体地说,表达式的结果永远不是引用。在这种情况下,注意示例中结果之间的差异。

#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
}

1
请包含示例的其余部分!这是最简洁的答案,但您需要阅读另一个才能理解它... - Luke Worth
1
那第一句话正是我在寻找的。谢谢。 - Victor Eijkhout
你的例子似乎与你的陈述“表达式的结果永远不是引用”相矛盾,因为bar()是一个表达式,而该示例显示它的结果是一个引用。 - igel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接