什么是右值,左值,x值,glvalues和prvalues?对rvalues/lvalues的分类进行了很好的概述,最近对该问题的回答之一(/sf/answers/668701631/)强调了prvalues"喜欢"旧式rvalues的观点而新的xvalues允许"左值"行为.
但是,请考虑以下代码:
class X {};
X foo() { return X(); }
int main()
{
foo() = X(); // foo() is a prvalue that successfully appears on the lhs
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,表达式foo()是出现在左侧的prvalue,并接受赋值.
这让我思考 - "xvalues"与"prvalues"的逻辑不同,因为xvalues(它们是glvalues)可能出现在左侧,似乎被这个例子打破了.在这里,我们有一个prvalue - 这不是一个glvalue - 成功地出现在lhs并接受任务.
(注意:在POD的情况下,上面的例子不会编译,所以对于POD,xvalues和prvalues之间的区别似乎是有意义的.因此,这个问题特别针对非POD类型.)
那么,xvalue和prvalue之间的允许使用或行为的真正区别是什么,这需要将这种区别写入标准?差异的一个例子是一个很好的替代答案.
附录
Pubby的评论是正确的.prvalue的生命周期由编译器扩展,但xvalue的生命周期不是.
所以,这是一个问题的答案:
请考虑以下代码:
// ***
// Answer to question, from Pubby's comment
// ***
class X
{
public:
X() : x(5) {}
int x;
};
X foo() { return X(); }
X&& goo() { return …Run Code Online (Sandbox Code Playgroud) 让我们有一个名为Y的函数重载:
void Y(int& lvalue)
{ cout << "lvalue!" << endl; }
void Y(int&& rvalue)
{ cout << "rvalue!" << endl; }
Run Code Online (Sandbox Code Playgroud)
现在,让我们定义一个像std :: forward一样的模板函数
template<class T>
void f(T&& x)
{
Y( static_cast<T&&>(x) ); // Using static_cast<T&&>(x) like in std::forward
}
Run Code Online (Sandbox Code Playgroud)
现在看看main()
int main()
{
int i = 10;
f(i); // lvalue >> T = int&
f(10); // rvalue >> T = int&&
}
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,输出是
lvalue!
rvalue!
Run Code Online (Sandbox Code Playgroud)
现在回到模板功能f()并替换static_cast<T&&>(x)为static_cast<T>(x).让我们看看输出:
lvalue!
rvalue!
Run Code Online (Sandbox Code Playgroud)
一样的!为什么?如果它们是相同的,那么为什么std::forward<>从返回一个投x来 …
我知道下面写的代码是非法的
void doSomething(std::string *s){}
int main()
{
doSomething(&std::string("Hello World"));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
原因是我们不允许获取临时对象的地址.但我的问题是为什么?
让我们考虑以下代码
class empty{};
int main()
{
empty x = empty(); //most compilers would elide the temporary
return 0;
}
Run Code Online (Sandbox Code Playgroud)
公认的答案在这里提到
"通常编译器会将临时和副本构造为两个对象,它们位于内存的完全相同的位置,并避免复制."
根据声明,可以得出结论,临时存在于某个内存位置(因此可能已经采用了它),编译器决定通过在临时存在的同一位置创建就地对象来消除临时对象. .
这是否与临时地址不能采取的事实相矛盾?
我还想知道如何实现返回值优化.有人可以提供与RVO实施相关的链接或文章吗?
允许这样做的背后的设计理由是什么?
const Foo& a = function_returning_Foo_by_value();
Run Code Online (Sandbox Code Playgroud)
但不是这个
Foo& a = function_returning_Foo_by_value();
Run Code Online (Sandbox Code Playgroud)
?
第二行可能出现什么问题(第一行不会出错)?
(答案提示.)
鉴于N3290,§7.1.6.2p4,其中列表项目未编号,但为了方便我们编号:
decltype(e)表示的类型定义如下:
- 如果e是未表示的id-expression或未加密的类成员访问(5.2.5),则decltype(e)是e所命名的实体的类型.如果没有这样的实体,或者如果e命名了一组重载函数,那么该程序就会形成错误;
- 否则,如果e是x值,则decltype(e)是T &&,其中T是e的类型;
- 否则,如果e是左值,则decltype(e)是T&,其中T是e的类型;
- 否则,decltype(e)是e的类型.
decltype(0 + 0)指定的类型是什么?
第1项不适用,2可能,但如果没有,则3不适用,4将是结果.那么,什么是xvalue,是0 + 0和xvalue?
§3.10p1:
xvalue("eXpiring"值)也指对象,通常接近其生命周期的末尾(例如,可以移动其资源).xvalue是涉及rvalue引用的某些表达式的结果(8.3.2).
我在§8.3.2中没有看到任何有用的内容,但我知道"0 + 0"不涉及任何右值引用.文字0是一个prvalue,它是"一个不是xvalue的rvalue"(§3.10p1).我相信"0 + 0"也是一个prvalue.如果这是真的,"decltype(0 + 0)"将是int(不是int &&).
我的解释错过了什么吗?这段代码是否格式良好?
decltype(0 + 0) x; // Not initialized.
Run Code Online (Sandbox Code Playgroud)
该代码编译在GCC 4.7.0 20110427和Clang 2.9(主干126116)上.例如,如果decltype指定了int &&类型,那么它将不会格式良好.
通常,rvalues可以绑定到const引用(const SomeType&).它内置于语言中.但是,std::reference_wrapper<const T>不接受rvalue作为其构造函数参数,因为故意删除了相应的重载.这种不一致的原因是什么?std::reference_wrapper当我们必须传递值但希望保留引用语义时,"广告"作为引用变量的替代.
换句话说,如果const &绑定的右值被认为是安全的,因为它内置于语言中,为什么C++ 11的设计者不允许包含rvalues std::reference_wrapper<const T>?
什么时候会派上用场,你可能会问.例如:
class MyType{};
class Foo {
public:
Foo(const MyType& param){}
};
class MultiFoo {
public:
MultiFoo(std::initializer_list<std::reference_wrapper<const MyType>> params){}
};
int main()
{
Foo foo{MyType{}}; //ok
MultiFoo multiFoo{MyType{}, MyType{}}; //error
}
Run Code Online (Sandbox Code Playgroud) 是否有可能重载的构造函数以某种方式调用类中的另一个构造函数,类似于下面的代码?
class A {
public:
A(std::string str) : m_str(str) {}
A(int i) { *this = std::move(A(std::to_string(i))); }
std::string m_str;
};
Run Code Online (Sandbox Code Playgroud)
上面的代码有效,但我担心在构造函数中调用它可能会导致未定义的行为.
如果确实如此,请解释原因并提出更好的选择?
我试图理解C++ 11中的左值和右值.所以我写了一个测试代码:
int x = 10;
int foo() { return x; }
int& bar() { return x; }
int&& baz() { return 10; }
int main() {
int& lr1 = 10; // error: lvalue references rvalue
int& lr2 = x; // ok
int& lr3 = foo(); // error: lvalue references rvalue
int& lr4 = bar(); // ok
int& lr5 = baz(); // error: lvalue references rvalue
int&& rr1 = 10; // ok
int&& rr2 = x; // error: rvalue references …Run Code Online (Sandbox Code Playgroud) 左值是绑定到存储器的确定区域的值,而右值是表达式值,其存在是临时的,并且不一定是指存储器的确定区域.只要在预期rvalue的位置使用左值,编译器就会执行左值到右值的转换,然后继续进行求值.
http://www.eetimes.com/discussion/programming-pointers/4023341/Lvalues-and-Rvalues
每当我们构造一个临时(匿名)类对象或从函数返回一个临时类对象时,虽然该对象是临时的,但它是可寻址的.但是,对象仍然是有效的右值.这意味着当编译器期望使用左值时,对象是a)可寻址的右值或b)从左值隐式转换为右值.
例如:
class A
{
public:
int x;
A(int a) { x = a; std::cout << "int conversion ctor\n"; }
A(A&) { std::cout << "lvalue copy ctor\n"; }
A(A&&) { std::cout << "rvalue copy ctor\n"; }
};
A ret_a(A a)
{
return a;
}
int main(void)
{
&A(5); // A(5) is an addressable object
A&& rvalue = A(5); // A(5) is also an rvalue
}
Run Code Online (Sandbox Code Playgroud)
我们还知道函数返回的临时对象(在下面的例子中a)是lvalues作为这个代码段:
int main(void)
{
ret_a(A(5));
}
Run Code Online (Sandbox Code Playgroud)
产生以下输出:
int conversion …
显然,不允许在ref-qualifiers上重载 - 如果删除&或者&&(只是标记,而不是它们的函数),这段代码将无法编译:
#include <iostream>
struct S {
void f() & { std::cout << "Lvalue" << std::endl; }
void f() && { std::cout << "Rvalue" << std::endl; }
};
int main()
{
S s;
s.f(); // prints "Lvalue"
S().f(); // prints "Rvalue"
}
Run Code Online (Sandbox Code Playgroud)
换句话说,如果您有两个具有相同名称和类型的函数,则必须定义两者,如果您定义其中任何一个.我认为这是故意的,但是原因是什么?例如,为什么不允许调用&&rvalues 的版本(如果已定义),以及f()以下变体中的其他所有内容的"primary" (反之亦然 - 尽管这会让人感到困惑):
struct S {
void f() { std::cout << "Lvalue" << std::endl; }
void f() && { std::cout << "Rvalue" …Run Code Online (Sandbox Code Playgroud) c++ ×10
rvalue ×10
c++11 ×6
lvalue ×3
const ×2
reference ×2
temporary ×2
xvalue ×2
constructor ×1
decltype ×1
overloading ×1
static-cast ×1
this ×1