C++ 常量引用与左值

C++ 常量引用与左值,c++,lvalue,rvalue,C++,Lvalue,Rvalue,我们不能写int&ref=40,因为我们需要在右边写lvalue。但是我们可以编写const int&ref=40。为什么这是可能的?40是rvalue而不是lvalue 我知道这是个例外,但为什么呢?语言中有一条规则允许将常量左值引用绑定到右值。该规则的主要原因是,如果它不存在,则必须提供不同的函数重载,才能将临时变量用作参数: class T; // defined somewhere T f(); void g(T const &x); 有了这个规则,你就可以做g(f()),如果

我们不能写
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
重载,这个重载接受一个右值(这是从右值引用在语言中根本不存在的时候开始的!)

为什么可能

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");
此临时变量只能绑定到常量引用。

正如Stroustrup所说:

常量T&的初始值设定项不必是左值,甚至不必是类型 T.在这种情况下:

[1] 首先,必要时应用到T的隐式类型转换

[2] 然后,将结果值放入临时变量 T型

[3] 最后,这个临时变量用作 初始化器


因此,当您键入
const int&ref=40
时,将在后台创建临时int变量,ref将绑定到此临时变量。

您可以将右值绑定到const引用。该语言保证绑定对象一直存在到引用范围结束,甚至静态调用正确的析构函数。例如,在ScopeGuard实现()中使用它,可以在不支付虚拟方法调用费用的情况下实现类似于虚拟析构函数的行为。

非常量引用也是如此…@DavidRodríguez dribea不是提供的第二种情况这正是编译器将为您做的,它将调用映射到:
std::string\uu tmp(“something”);f(_tmp)
。这是因为您不能将此作为允许
常量
引用(单独)的理由,Solaris CC和VS都支持通过执行完全相同的操作将非常量引用绑定到临时引用,这一事实证明了这一点。@DavidRodríguez Dribea是真的,editedI仍然不确定为什么会这样做:-(@KerrekSB:我猜最初的原因是,这样你就可以无动于衷地编写
T const&
T const
,两者都可以工作,前者在表达式生成引用(无副本)时效率更高。这确实很不幸,因为它让很多but溜进了。@KerrekSB:我想我已经在链接问题中解决了这个问题。这是在一般上下文中允许绑定的副作用(不仅仅是在函数参数中)并且不希望它在每次使用时都导致未定义的行为。如果没有生存期扩展:
T const&x=f();
然后再使用任何odr
x
将是未定义的行为,这就提出了一个问题:为什么首先允许这样做?因此,要么提供一致性(在任何上下文中都有引用绑定)使用寿命扩展,或者你接受不一致,并且不需要寿命扩展。语言使用了前者。为了尽量简洁,我把它完全弄糟了。阅读上面的odr用法不是odr用法,而是访问真实对象的任何用法(左值-右值转换,访问任何成员…)嗯,我仍然不相信(正如我在另一篇文章中所说的)-“完整表达式的结束”规则似乎工作得很好,生命周期扩展也同样令人惊讶(典型的例子是通过另一个返回引用的函数传递引用)。因此我仍然愿意接受贡献:-)编译器在这件事上没有选择的余地。语言规则决定了生命周期。