根据What are rvalues, lvalues, xvalues, glvalues, and prvalues?和其他一些解释,我的理解是xvalue是具有标识并且可以安全移动(或如此标记)的表达式。
诸如thisthis之类的一些文本说,如果函数f()的返回类型是rvalue reference,则表达式f()是xvalue。例如:

int&& f() {
  return 1;
}

int main() {
  f(); // xvalue
  2; // prvalue
}
我的困惑是,因为f()的起源是文字1,对我来说f()似乎没有身份,因此我无法理解它如何变成xvalue。如果1具有身份,为什么说2没有身份并且是prvalue?如果从作为右值引用的函数返回的prvalue是否突然具有“身份”?
编辑
有人指出f()返回一个悬空的引用,但我希望我的观点仍然有意义。
编辑2
好吧,在阅读了(非常有帮助的)评论之后,看来这似乎没有意义吗?

最佳答案


是的,实际上。标准几乎完全说明了这一点:
[conv.rval]

该临时对象虽然存在,但肯定具有“身份”。当然,这种转换的结果不再是prvalue,因此也许我们不应该说prvalue“获得了身份”。请注意,这也是有效的,也是由于临时实现:

(int&&)1; // This is different from f(), though, because that reference is dangling but I believe this one isn't (lifetime extension of a temporary by binding to a reference applies here but is suppressed for a return)
请注意,return语句的操作数与实际返回的内容不必相同。您给一个int prvalue,您需要一个int xvalue,return通过具体化一个临时值来使其工作。由于不匹配,它没有义务失败。不幸的是,当return语句结束时,该临时变量立即被销毁,留下xvalue晃来晃去,但是,在绑定(bind)返回的引用和销毁临时变量之间的那一刻,是的,右值引用确实引用了具有其自身标识的对象。
prvalue实现的其他示例,因此您可以将引用绑定(bind)到它们:
int &&x = 1; // acts just like int x = 1 except for decltype and similar considerations
int const &y = 1; // ditto but const and also available in older C++ versions

// in some imaginary API
void install_controller(std::unique_ptr<controller> &&new_controller) {
     if(can_replace_controller()) current_controller = std::move(new_controller);
}
install_controller(std::make_unique<controller>("My Controller"));
// prvalue returned by std::make_unique materializes a temporary, binds to new_controller
// might be moved from, might not; in latter case new pointer (and thus object)
// is destroyed at full-expression end (at the semicolon after the function call)
// useful to take by reference so you can do something like
auto p = std::make_unique<controller>("Perseverant Controller");
while(p) { wait_for_something(); install_controller(std::move(p)); }
return的其他示例并不简单:
double d(int x) { return x; }
// int lvalue given to return but double prvalue actually returned! the horror!

struct dangerous {
    dangerous(int x) { launch_missiles(); }
};
dangerous f() { return 1; }
// launch_missiles is called from within the return!

std::vector<std::string> init_data() {
    return {5, "Hello!"};
}
// now the operand of return isn't even a value/expression!
// also, in terms of temporaries, "Hello!" (char const[7] lvalue) decays to a
// char const* prvalue, converts to a std::string prvalue (initializing the
// parameter of std::string's constructor), and then that prvalue materializes
// into a temporary so that it can bind to the std::string const& parameter of
// std::vector<std::string>'s constructor

10-04 15:04