我们不能写成int& ref = 40
,因为我们需要右边的lvalue
。但是我们可以写const int& ref = 40
。为什么呢?因为40是一个rvalue
而不是一个lvalue
。
我知道这是一个例外,但是为什么呢?
在该语言中有一条规则允许将常量左值引用绑定到右值。这个规则的主要原因是,如果没有它,那么你将不得不提供不同的函数重载来使用临时变量作为参数:
class T; // defined somewhere
T f();
void g(T const &x);
有了这个规则,你可以执行g(f())
,如果没有这个规则,你需要创建一个不同的g
重载函数来接受一个rvalue(而这是在rvalue引用还不存在于语言中的时候!)。
为什么这是可能的?
这里的40是一个字面量。常量引用可以通过字面量和临时变量进行初始化,以延长它们的生命周期。编译器可以通过以下方式实现:
int const& ans = 40;
// transformed:
int __internal_unique_name = 40;
int const& ans = __internal_unique_name;
另一种情况是当您拥有一个函数时,例如:
void f( std::string const& s);
如果您想调用它,可以使用以下方式:
f( "something");
这个临时变量只能绑定到常量引用。
std::string __tmp("something"); f(__tmp);
。因为你可以这样做并不是允许const
引用(独自)的理由,这一事实已被Solaris CC和VS支持将非const引用绑定到临时变量所证明。 - David Rodríguez - dribeas
T const& _ = ...;
或者T const _ = ...;
而不必在意哪种方式会起作用,前者在表达式生成引用时更为高效(没有副本)。然而,这确实非常不幸,因为它会让很多错误通过。 - Matthieu M.T const& x = f();
,然后跟随任何对x
的odr-use将会是未定义的行为,这就引出了为什么要首先允许它的问题?因此,要么您提供一致性(引用在任何上下文中绑定)与生命周期延长,要么您接受不一致性,而不需要生命周期延长。语言采取了前者。 - David Rodríguez - dribeasT const & r = f();
这样的语句。完整表达式结束点 在函数参数中可以正常工作,但在这种情况下,表达式以分号结束。如果您允许这个表达式而不延长变量r
的生命周期,那么对r
的任何使用都将是未定义行为。 - David Rodríguez - dribeas