c++模板问题

C++ Templates Question

本文关键字:问题 c++      更新时间:2023-10-16

谁能解释一下下面代码的输出?

#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

我不明白为什么。既然(a+b)不返回const a对象,那么最后一次赋值调用不应该激活第一个版本吗?

表达式不仅有值和类型,而且还有值类别。这个类别可以是

  • 左值:这些表达式通常指向已声明的对象、引用、函数或指针的解引用结果。
  • 一个xvalue:这些是生成未命名的右值引用的结果。右值引用是由T&&而不是T&创建的。它们是c++ 11的概念,您可以在这里忽略它们。仅为完整起见而提及。
  • 右值:这些是强制转换到非引用类型(如A(10))或计算/指定值(如422 + 3)的结果。

左值引用需要一个左值表达式来初始化。也就是说,以下内容无效:

A &x = A(10);

背后的原因是,只有左值表达式指的是适合并打算在较长时间内保持存活的东西,而不仅仅是在初始化期间。例如,声明的对象在退出其块(如果它是一个局部非静态变量)或直到程序结束(如果它在函数和类之外声明)之前都是活动的。右值表达式A(10)指向一个在初始化完成时已经死亡的对象。如果你说下面的话,它将没有任何意义,因为像10这样的纯值根本没有地址,但是引用需要它们绑定的某种身份,实际上是通过在编译器内部获取目标地址来实现的

int &x = 10; // makes totally no sense

但是对于const引用,c++有一个后门。当使用右值初始化时,如果表达式引用对象,则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&参数类型的版本。

(a + b)返回一个临时对象,因此只能绑定到常量引用。

a+b返回一个临时的,如果你被允许捕获一个对它的非const引用,你将能够改变它,然后呢?临时对象超出了作用域,对其所做的更改永远无法被应用程序捕获。在c++ 03中,临时变量将被绑定到const引用类型。

顺便说一下,这与模板无关。重写你的例子,使用直接的"A",你会看到相同的行为。