为什么命名右值引用是左值表达式?
Why is a named rvalue reference an lvalue expression?
我知道命名引用是左值:
int x = 1;
int& ref1 = x;
int&& ref2 = std::move(x);
我已经阅读了解释 — 那是因为我们可以获取那些 ref1
和 ref2
的地址。
但是当我们获取引用的地址时,我们实际上获取的是被引用对象的地址,不是吗?所以这个解释似乎并不正确。
那么为什么命名引用是左值?
这是 Scott Meyers 的书 "Effective Modern C++" 中的解释:
In fact, T&&
has two different meanings. One is rvalue reference, of course. Such references behave exactly the way you expect: they bind only to rvalues, and their primary raison d’être is to identify objects that may be moved from.
void f(Widget&& param); // rvalue reference
Widget&& var1 = Widget(); // rvalue reference
auto&& var2 = var1; // not rvalue reference
template<typename T>
void f(std::vector<T>&& param); // rvalue reference
template<typename T>
void f(T&& param); // not rvalue reference
The other meaning for T&&
is either rvalue reference or lvalue reference. Such references look like rvalue references in the source code (i.e., T&&
), but they can behave as if they were lvalue references (i.e., T&
). Their dual nature permits them to bind to rvalues (like rvalue references) as well as lvalues (like lvalue references). Furthermore, they can bind to const
or non-const
objects, to volatile
or non-volatile
objects, even to objects that are both const
and volatile
. They can bind to virtually anything. Such unprecedentedly flexible references deserve a name of their own. I call them universal references.
根据 [expr.prim.id.unqual](8.1.4.1 不合格名称):
[...] The expression is an lvalue if the entity is a function,
variable, or data member and a prvalue otherwise; it is a bit-field if
the identifier designates a bit-field ([dcl.struct.bind]).
根据 [basic]/6:
A variable is introduced by the declaration of a reference other
than a non-static data member or of an object. The variable's name, if
any, denotes the reference or object.
宣言
int&& ref2 = std::move(x);
是一个"declaration of a reference other than a non-static data member."因此,ref2
表示的实体是一个变量。所以表达式 ref2
是一个左值。
该解释只是一种简化。左值不是由 "something you can take the address of" 定义的,而是由一组关于表达式值类别的特定规则定义的。这些规则经过精心构建,以形成一种 self-consistent 语言,在这种语言中,一切都可以合理地整齐地组合在一起。
话虽这么说, 的解释很适合这里,如果您通过写作 ref1
来考虑这一点,那么您并不是真正在命名 "the reference",而是所指的事物。这就是引用的魔力:您应该将它们视为别名而不是实体本身。
围绕此存在一些抽象泄漏(特别是成员引用),但这就是要点。
您应该忘掉 "the reference is an lvalue" 这样的概念,而应该考虑 表达式 。对象有类型;表达式具有值类别。
我知道命名引用是左值:
int x = 1;
int& ref1 = x;
int&& ref2 = std::move(x);
我已经阅读了解释 — 那是因为我们可以获取那些 ref1
和 ref2
的地址。
但是当我们获取引用的地址时,我们实际上获取的是被引用对象的地址,不是吗?所以这个解释似乎并不正确。
那么为什么命名引用是左值?
这是 Scott Meyers 的书 "Effective Modern C++" 中的解释:
In fact,
T&&
has two different meanings. One is rvalue reference, of course. Such references behave exactly the way you expect: they bind only to rvalues, and their primary raison d’être is to identify objects that may be moved from.void f(Widget&& param); // rvalue reference Widget&& var1 = Widget(); // rvalue reference auto&& var2 = var1; // not rvalue reference template<typename T> void f(std::vector<T>&& param); // rvalue reference template<typename T> void f(T&& param); // not rvalue reference
The other meaning for
T&&
is either rvalue reference or lvalue reference. Such references look like rvalue references in the source code (i.e.,T&&
), but they can behave as if they were lvalue references (i.e.,T&
). Their dual nature permits them to bind to rvalues (like rvalue references) as well as lvalues (like lvalue references). Furthermore, they can bind toconst
or non-const
objects, tovolatile
or non-volatile
objects, even to objects that are bothconst
andvolatile
. They can bind to virtually anything. Such unprecedentedly flexible references deserve a name of their own. I call them universal references.
根据 [expr.prim.id.unqual](8.1.4.1 不合格名称):
[...] The expression is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise; it is a bit-field if the identifier designates a bit-field ([dcl.struct.bind]).
根据 [basic]/6:
A variable is introduced by the declaration of a reference other than a non-static data member or of an object. The variable's name, if any, denotes the reference or object.
宣言
int&& ref2 = std::move(x);
是一个"declaration of a reference other than a non-static data member."因此,ref2
表示的实体是一个变量。所以表达式 ref2
是一个左值。
该解释只是一种简化。左值不是由 "something you can take the address of" 定义的,而是由一组关于表达式值类别的特定规则定义的。这些规则经过精心构建,以形成一种 self-consistent 语言,在这种语言中,一切都可以合理地整齐地组合在一起。
话虽这么说, 的解释很适合这里,如果您通过写作 ref1
来考虑这一点,那么您并不是真正在命名 "the reference",而是所指的事物。这就是引用的魔力:您应该将它们视为别名而不是实体本身。
围绕此存在一些抽象泄漏(特别是成员引用),但这就是要点。
您应该忘掉 "the reference is an lvalue" 这样的概念,而应该考虑 表达式 。对象有类型;表达式具有值类别。