C++ C++;11:为什么右值引用参数隐式转换为左值

C++ C++;11:为什么右值引用参数隐式转换为左值,c++,c++11,rvalue-reference,C++,C++11,Rvalue Reference,下面是我问题的简单代码 void overloaded (int&& x) { cout << "[rvalue]"; } template <class T> void fn (T&& x) { overloaded(x); } int main() { fn(0); return 0; } 我在这里感到困惑,x作为右值引用传递到fn()。但是为什么重载()调用fn()会抱怨x是一个左值呢?其中,fn的x参数

下面是我问题的简单代码

void overloaded (int&& x) {
  cout << "[rvalue]";
}

template <class T>
void fn (T&& x) {
  overloaded(x);
}

int main() {
    fn(0);
    return 0;
}

我在这里感到困惑,
x
作为右值引用传递到
fn()
。但是为什么
重载()
调用
fn()
会抱怨
x
是一个左值呢?

其中,
fn
x
参数不是一个r值引用(是的)

第二,当你给一个对象命名时,该名称不是一个r值,除非用
std::move
(使其始终成为r值引用)或
std::forward
(在通用引用的情况下将其转换回其原始类型)显式“固定”。如果要避免投诉,请使用
std::forward
作为原始类型转发:

template <class T>
void fn (T&& x) {
  overloaded(std::forward<T>(x));
}
模板
无效fn(T&x){
重载(std::forward(x));
}
您没有“将
x
作为右值引用传递”,因为该语句没有真正意义

下面是一些关于C++中类型和表达式分类的事实:

  • 有对象类型和引用类型。(还有其他几种类型。)

  • 变量可以是对象或引用

  • 表达式有类型,这些类型(几乎)总是对象类型,而不是引用。(这就是为什么您的“传递某物作为引用”的语句没有意义;您传递参数,并且参数始终是表达式。)表达式可以是左值、xvalue或prvalue

  • 左值引用绑定到左值。右值引用绑定到右值。(右值是prvalue或xvalue。)

  • 命名变量或参数的id表达式是左值。(如果你愿意的话,这是一个“有名字的东西”或“地点”。)

因此,在表达式
重载(x)
中,子表达式
x
是一个左值,它不会绑定到期望右值的函数(由于其右值引用参数)

“左值引用”和“右值引用”中的“l”和“r”指的是引用可以绑定到的值的类别,而不是命名此引用类型变量的id表达式的类别


您可以将左值转换为右值,方法是将其转换为xvalue;这可以方便地封装到类型推断强制转换帮助器
std::move

“一旦命名变量,它就不再是r值”没有意义。右值是表达式类别,而不是变量或对象。对象没有表达式类别;引用该变量的表达式具有表达式类别,同一对象可以具有引用该变量的不同表达式类别it@M.M我欢迎更准确的措辞,不需要完全去理解C++规范来理解它。关键是,只要将r值绑定到名称(即使是声明为
&&
)上,如果没有额外的“努力”(
std::move
/
std::forward
),它就不能用作r值。您是否更喜欢“当您给一个对象命名时,它就不再是r值”?对象在任何时候都不是左值或右值。表示对象的表达式可以是左值或右值。将引用绑定到对象不会更改对象的任何属性(除了延长其生命周期之外)。名称
x
是左值;它不会变成一个,并且作为参数给出的对象不会“停止成为右值”,因为它从来都不是右值,而是(现在仍然是)一个objectRe。最近的编辑,
x
总是一个左值,没有“除非”。类似于std::forward(x)的代码是与x不同的表达式。整个段落的内容就好像实际对象具有左值或右值的属性一样。
template <class T>
void fn (T&& x) {
  overloaded(std::forward<T>(x));
}