在C ++中,哪些类(左值,右值,左值等)可以生成临时types的expression式?
以下是一些示例代码:
#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()这些expression式的事实是否意味着它们具有身份? 或者,我不能应用默认的&操作符(我的意思是非重载的)的事实意味着他们没有身份,因此是预测?
通过产生它的expression式产生的价值的可变性与这种价值分类是正交的,这也是公平的吗?
每个expression式都是唯一的一个expression式:
- 左值
- x值
- prvalue
作为左值和右值的expression式的联合被统称为“ glvalues” 。
作为xvalues和prvalues的expression式的联合统称为rvalues 。
因此xvalueexpression式被称为glvalue和rvalues。
在Alf的答案中find的方便的图正确地说明了我用上面的单词描述的关系,也可以在C ++标准C ++ 11及更高版本的第3.10节中find。
我上面所说的一切,我怀疑OP是否已经知道,只是从这个问题的标题的措辞。
琐事:
Bjarne Stroustrup发明了这种expression式的分类,这样做可能将整个右值引用提议保存在核心工作组中。 我将永远感激。
我所添加的是一种方法,可以自己发现任何expression式落入三个底部分类类别中的哪一个:左值,左值或右值。
#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
在使用这个function时需要注意一点:
decltype(variable_name)
将给出variables名称的声明types。 如果要在使用variable_name
(与其声明的types相反)时发现expression式的值类别,则在decltype
使用时,需要在(variable_name)
周围添加额外的括号。 那是:
decltype((variable_name))
是expression式 variable_name
的types,而不是variable_name
的声明types。
例如给出:
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
从types名称转换为expression式。 现在输出结果是:
foo is a lvalue expression of type Foo
如果您不确定是否需要添加圆括号或者不能得到正确的答案,那么只需添加它们即可。 添加它们不会使正确的答案错误 – 除非您正在寻找获取variables的声明types,而不是expression式的types。 在后一种情况下,你需要一个密切相关的函数: type_name<T>()
。
C ++中的任何expression式都是左值或右值 。 因此,你要求的分类是右值。 为此,请在C ++ 11标准§3.10/ 1中查看显示分类树的图。
有关更多信息(无需深入研究标准),请参阅什么是右值,左值,…。
关于
“是否像Foo(5)rvalues或prvalue”
一个价值是必要的 – 一个左值 – 因为它不可能是一个左值。
一个prvalue “(”纯“右值)是一个不是一个xvalue的右值”,而xvalue是“某些涉及右值引用的expression式的结果”构造函数调用不会产生右值引用,因此它不是一个xvalue 。 所以右值是一个值,一个纯粹的右值。