有人可以解释以下代码的输出吗?
#include <iostream>
template <class T>
void assign(T& t1, T& t2){
std::cout << "First method"<< std::endl;
t1 = t2;
}
template <class T>
void assign(T& t1, const T& t2) {
std::cout << "Second method"<< std::endl;
t1 = t2;
}
class A
{
public:
A(int a) : _a(a) {};
private:
friend A operator+(const A& l, const A& r);
int _a;
};
A operator+(const A& l, const A& r)
{
return A(l._a + r._a);
}
int main ()
{
A a = 1;
const A b = 2;
assign(a, a);
assign(a, b);
assign(a, a + b);
}
输出
First method
Second method
Second method
我不明白为什么。最后一次调用assign不应该激活第一个版本,因为(a + b)不返回const A对象吗?
答案 0 :(得分:7)
(a + b)
返回一个临时对象,因此只能绑定到一个常量引用。
答案 1 :(得分:7)
表达式不仅具有值和类型,还具有值 category 。此类别可以是
T&&
而非T&
创建。它们是C ++ 11概念,你可以在这里忽略它们。仅为了完整起见而提及。A(10)
)或计算/指定值的转换结果,如42
或2 + 3
。 左值引用需要左值表达式进行初始化。也就是说,以下内容无效:
A &x = A(10);
这背后的原因是只有左值表达式指的是适合并且用于保持活动的时间比仅在初始化期间更长的时间。就像,声明的对象一直存在,直到退出它的块(如果它是一个本地非静态变量)或直到程序结束(如果它被声明在函数和类之外)。 rvalue表达式A(10)
指的是在初始化完成时已经死亡的对象。如果你说了以下内容,它就没有任何意义,因为像10
这样的纯值根本没有地址,但是引用需要它们绑定的某种身份,这在实践中是通过在编译器内部获取其目标地址来实现
int &x = 10; // makes totally no sense
但对于const引用,C ++有一个后门。当使用prvalue初始化时,如果表达式引用对象,则const左值引用将自动延长对象的生命周期。如果表达式具有非对象值,C ++将创建一个具有该表达式值的临时对象,并延长该临时对象的生命周期,将该引用绑定到该临时对象:
// lifetime of the temporary object is lengthened
A const& x = A(10);
// lifetime of the automatically created temporary object is lengthened
int const& x = 10;
您的情况会怎样?
现在,在您的情况下编译器,因为您提供了一个临时对象,将选择具有A const&
参数类型而不是A&
参数类型的版本。
答案 2 :(得分:3)
a+b
返回一个临时的,如果你被允许捕获一个非const引用,你就可以改变它,然后呢?临时超出范围,应用程序永远无法捕获对其执行的更改。在C ++ 03中,temporaries将绑定到const引用类型。
顺便说一句,这与模板无关。重写你的例子直接使用'A',你会发现同样的行为。