【问题标题】:rvalue reference (expression) returned by function is xvalue - but no identity?函数返回的右值引用(表达式)是 xvalue - 但没有标识?
【发布时间】:2020-06-25 19:56:09
【问题描述】:

根据What are rvalues, lvalues, xvalues, glvalues, and prvalues? 和其他一些解释,我的理解是 xvalue 是具有标识并且被安全移动(或被标记)的表达式。

thisthis 这样的一些文本说,如果函数f() 的返回类型是右值引用,那么表达式f() 就是xvalue。例如:

int&& f() {
  return 1;
}

int main() {
  f(); // xvalue
  2; // prvalue
}

我的困惑是,因为f() 的起源是文字1,对我来说f() 似乎没有身份,因此我无法理解它是如何变成xvalue 的。如果1有身份,为什么说2没有身份,是prvalue?如果prvalue作为右值引用从函数返回,它是否突然具有“身份”?

编辑

有人指出f() 返回一个悬空引用,但我希望我的观点仍然有意义。

编辑2

嗯,看完了(很有帮助的)cmets,好像说不通吧?

【问题讨论】:

标签: c++


【解决方案1】:

如果纯右值作为右值引用从函数返回,它会不会突然具有“身份”?

是的,实际上。标准几乎直截了当地说:

[conv.rval]

T 类型的纯右值可以转换为T 类型的 xvalue。此转换通过评估以临时对象作为其结果对象的纯右值,从纯右值初始化 T 类型的临时对象 ([class.temporary]),并生成一个表示临时对象的 xvalue。

这个临时对象,虽然存在,但肯定有“身份”。当然,这种转换的结果不再是纯右值,所以也许我们不应该说纯右值“得到一个身份”。请注意,这也有效,也是因为临时实现:

(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 悬空,但是,在返回的引用被绑定和临时被销毁之间的那一刻,是的,右值引用确实引用了一个带有它的对象自己的身份。

实现纯右值的其他示例,以便您可以将引用绑定到它们:

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

【讨论】:

    【解决方案2】:

    在这里我尝试在阅读给定的 cmets 后总结我的理解。

    返回右值引用的全部目的是以某种方式使用它,因此不考虑返回指向函数本地对象的右值引用,该对象在函数返回时已经无效(好吧,我当然委员会确实考虑过这一点,但不是预期的用途)。

    因此,如果我有一个函数T&amp;&amp; f() { /.../ return val; },即使在f() 返回之后,val 也应该以它的身份定位在某个地方,否则它会悬空,这仅仅是一个错误。因此,f() 有一个标识,xvalue 也有一个标识的意图是合理的。

    【讨论】:

      【解决方案3】:

      老实说,我发现“拥有身份”的整个概念有些实际意义。

      我的想法是这样的:

      • prvalue 是创建对象的表达式。

      • 右值是表示临时对象(或被认为是临时对象,例如因为它是std::moved)的表达式。

      • 左值是表示非临时对象(或被认为是非临时对象)的表达式。

      int &amp;&amp;f() {...} 的调用不会创建新对象(至少如果我们忽略函数体,只看函数调用机制本身),因此结果不是纯右值(但它显然是rvalue,因此它也是一个 xvalue)。

      另一方面,对int f() {...} 的调用无条件地创建一个对象(临时的int;与函数体无关),因此它是一个prvalue。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-29
        • 2020-09-21
        相关资源
        最近更新 更多