以下是一些示例代码:
#include <iostream>
class Foo
{
public:
explicit Foo(int x) : data(x) {};
Foo& operator++()
{
data += 1;
return *this;
}
void *get_addr()
{
return (void*)this;
}
friend Foo operator + (const Foo& lhs, const Foo& rhs);
friend std::ostream& operator << (std::ostream& os, const Foo& f);
private:
int data;
};
std::ostream& operator << (std::ostream& os, const Foo& f)
{
return (os << f.data);
}
Foo operator + (const Foo& lhs, const Foo& rhs)
{
return Foo(lhs.data + rhs.data);
}
void bar(Foo& f)
{
std::cout << "bar(l-value ref)" << std::endl;
}
void bar(const Foo& f)
{
std::cout << "bar(const l-value ref)" << std::endl;
}
void bar(Foo&& f)
{
std::cout << "bar(r-value ref)" << std::endl;
}
int main()
{
// getting the identity of the object
std::cout << Foo(5).get_addr() << std::endl; // Can write &Foo(5)
// by overloading &
// overload resolution
bar(Foo(5)); // prints r-value ref
// default copy assignment
std::cout << (Foo(78) = Foo(86)) << std::endl; // prints 86
// mutating operations
std::cout << (++Foo(5)) << std::endl; // prints 6
// more mutating operations
std::cout << (++(Foo(78) + Foo(86))) << std::endl; // prints 165
// overload resolution
bar((Foo(78) + Foo(86))); // prints r-value ref
}
表达式如Foo(5)prvalues或一般rvalues?我可以在这些表达式上调用get_addr()的事实是否意味着它们具有身份?或者我不能应用默认&amp; -operator(我的意思是非重载)意味着他们没有身份并因此是prvalues?
通过产生它的表达式对产生的值的可变性是否与这个值分类正交是否公平?
答案 0 :(得分:17)
每个表达式都是一个,只有一个:
作为左值和x值的表达式的并集统称为 glvalues 。
xvalues和prvalues的表达式联合统称为 rvalues 。
因此,xvalue表达式被称为glvalues和rvalues。
在Alf的答案中找到的方便图表正确地说明了我用上述词语描述的关系,也可以在C ++标准的第3.10节中找到,C ++ 11及更高版本。
我上面所说的一切,我怀疑OP已经知道,只是从这个问题的标题的措辞。
花絮:
Bjarne Stroustrup发明了这种表达的分类,并且在 这样做可能保存了整个右值参考建议 在核心工作组中崩溃。我将永远感激。
我正在添加的是一种方法,可以自己发现任何表达式所属的三个底层分类类别中的哪一个:lvalue,xvalue或prvalue。
#include <type_traits>
#include <typeinfo>
#include <iostream>
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>
template <typename T>
std::string
expression_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
__cxxabiv1::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += "const ";
if (std::is_volatile<TR>::value)
r += "volatile ";
if (std::is_lvalue_reference<T>::value)
r = "lvalue expression of type " + r;
else if (std::is_rvalue_reference<T>::value)
r = "xvalue expression of type " + r;
else
r = "prvalue expression of type " + r;
return r;
}
上述功能可以像:
一样使用std::cout << "some_expression is a " << expression_name<decltype(some_expression)>() << '\n';
它将回答这个OP的问题。例如:
int main()
{
std::cout << "Foo(5) is a " << expression_name<decltype(Foo(5))>() << '\n';
std::cout << "Foo(5).get_addr() is a " << expression_name<decltype(Foo(5).get_addr())>() << '\n';
std::cout << "Foo(78) = Foo(86) is a " << expression_name<decltype(Foo(78) = Foo(86))>() << '\n';
std::cout << "++Foo(5) is a " << expression_name<decltype(++Foo(5))>() << '\n';
std::cout << "++(Foo(78) + Foo(86)) is a " << expression_name<decltype(++(Foo(78) + Foo(86)))>() << '\n';
std::cout << "Foo(78) + Foo(86) is a " << expression_name<decltype(Foo(78) + Foo(86))>() << '\n';
std::cout << "std::move(Foo(5)) is a " << expression_name<decltype(std::move(Foo(5)))>() << '\n';
std::cout << "std::move(++Foo(5)) is a " << expression_name<decltype(std::move(++Foo(5)))>() << '\n';
}
对我来说打印出来:
Foo(5) is a prvalue expression of type Foo
Foo(5).get_addr() is a prvalue expression of type void*
Foo(78) = Foo(86) is a lvalue expression of type Foo
++Foo(5) is a lvalue expression of type Foo
++(Foo(78) + Foo(86)) is a lvalue expression of type Foo
Foo(78) + Foo(86) is a prvalue expression of type Foo
std::move(Foo(5)) is a xvalue expression of type Foo
std::move(++Foo(5)) is a xvalue expression of type Foo
使用此功能时需要注意的一个方面:
decltype(variable_name)
将提供变量名称的声明类型。如果要在使用variable_name
时(而不是其声明的类型)发现表达式的值类别,则需要在使用(variable_name)
时添加额外的括号在decltype
。那就是:
decltype((variable_name))
是表达式 variable_name
的类型,而不是variable_name
的声明类型。
例如:
Foo&& foo = Foo(5);
std::cout << "foo is a " << expression_name<decltype(foo)>() << '\n';
这将错误地输出:
foo is a xvalue expression of type Foo
将额外的括号添加到decltype
:
std::cout << "foo is a " << expression_name<decltype((foo))>() << '\n';
将foo
从类型名称转换为表达式。现在输出是:
foo is a lvalue expression of type Foo
如果您不确定是否需要添加括号来获得正确的答案,那么只需添加它们即可。添加它们不会使错误的答案正确 - 除非您希望获得变量的声明类型,而不是表达式的类型。在后一种情况下,您需要一个密切相关的函数:type_name<T>()
。
答案 1 :(得分:7)
C ++中的任何表达式都是 左值或右值。因此,您要求的是rvalues的分类。为此,请参阅C ++ 11标准§3.10/ 1中显示分类树的图。
有关详细信息(无需深入研究标准),请参阅What are rvalues, lvalues, ...。
关于
“表达式如Foo(5)rvalues或prvalue”
prvalue是必要的rvalue - 因为它不太可能是左值。
prvalue “(”纯“rvalue)是一个不是xvalue的rvalue,而 xvalue 是”某些表达式涉及的结果“ rvalue references“构造函数调用不会产生右值引用,因此它不是xvalue。所以rvalue是一个prvalue,一个纯rvalue。