std::move 从值的返回引用中获取时的自动安全性

Safety of auto when std::move ing from a returned reference of a value

我需要一些保证,无论何时,分配或列表初始化一个 auto 类型的命名变量,

在表达式之后,原点超出范围,A 安全/B 不安全。示例代码:

#include <iostream>
#include <string>
#include <deque>
#include <utility>

int main() {
    std::deque<std::string> container {"foo"};
    auto elementB = container.front(); //B I assume this is unsafe
    auto elementA = std::move(container.front());//A I assume this is safe
    container.pop_front();

    std::cout << "A: " << elementA << " B: " << elementB  << "\n";
}

据我所知,表达式 B 生成赋值的左值权,因此 elementB 的类型是左值引用,又名 std::string&,因此不安全。

执行代码时 "A: foo B: " 的输出也表明了这一点。 ( https://ideone.com/wKKbdK ) 更新:对不起,我忘了我移动了它,所以我改变了顺序,现在输出是例外的,对不起。

然而,我不确定的更麻烦的事情是表达式 A:在 std::move 之后,我假设我得到了一个 xvalue,它既是右值又是左值,所以我不确定标准化行为是什么如果有的话,对于elementA的类型推导。

因为从左值我几乎可以肯定它是 UB,而左值是 glvalues,而 xvalues 是其中的一部分,那么 elementA 的类型将是 std::string&&,这是不安全的,对吧? (除非 const&& AFAIK 例外)

总结一下:elementA 的使用是安全的标准化行为吗?它的类型是什么?

您的代码没问题,因为 elementAelementB 都将 deduced 作为 string,而不是引用(string&string&&).

给定auto elementA,初始化器的引用部分将被忽略。如果你想 elementAelementB 作为参考,你必须明确指定,例如auto&& elementAauto& elementB.

您可能会感到困惑 value category with types; they're independent. The value category of the initializer, i.e. it's an lvalue or rvalue, won't affect type deduction。在您的代码中,给定 auto elementAelementA 将是 std::string 类型,给定 auto& elementA,其类型将是 std::string&,给定 auto&& elementA,它的类型将是 std::string&&

Is the usage of elementA safe standardized behaviour?

是的。

... and what will be its type?

它的类型将是 std::stringauto 类型推导类似于模板类型推导,包括删除引用的 "referenceness"。 std::move(container.front()) return 是一个 xvalue 的事实在这里并没有太大变化。它是一个 "expiring" 值,您可以 (a) move-construct 一个新对象(如您当前所做的那样)(b)将其绑定到 const 限定的引用或 (c) 绑定它变成 rvalue-reference。在这里,(b) 和 (c) 都有效但没有多大意义,因为它们掩盖了没有任何东西是 moved-from 的事实(感谢 @M.M 在这里纠正我)。示例:

auto elementA = std::move(container.front());
// Error, can't bind to non-const reference:
// auto& doesntWork = std::move(container.front());
auto&& thisWorks = std::move(container.front());
const auto& thisWorksToo = std::move(container.front());

请注意,正如@M.M 在评论中指出的那样,一旦遇到 container.pop_front();,最后两个引用将悬空。

另请注意,将 elementB 推导为 std::string 并不能帮助您取消引用 moved-from 对象(return by container.front()),你应该避免。