std::move 从值的返回引用中获取时的自动安全性
Safety of auto when std::move ing from a returned reference of a value
我需要一些保证,无论何时,分配或列表初始化一个 auto
类型的命名变量,
- A
std::move
()ed 返回了对变量的引用
- B 返回的变量引用
在表达式之后,原点超出范围,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 的使用是安全的标准化行为吗?它的类型是什么?
您的代码没问题,因为 elementA
和 elementB
都将 deduced 作为 string
,而不是引用(string&
或 string&&
).
给定auto elementA
,初始化器的引用部分将被忽略。如果你想 elementA
或 elementB
作为参考,你必须明确指定,例如auto&& elementA
或 auto& 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 elementA
,elementA
将是 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::string
。 auto
类型推导类似于模板类型推导,包括删除引用的 "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()
),你应该避免。
我需要一些保证,无论何时,分配或列表初始化一个 auto
类型的命名变量,
- A
std::move
()ed 返回了对变量的引用 - B 返回的变量引用
在表达式之后,原点超出范围,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 的使用是安全的标准化行为吗?它的类型是什么?
您的代码没问题,因为 elementA
和 elementB
都将 deduced 作为 string
,而不是引用(string&
或 string&&
).
给定auto elementA
,初始化器的引用部分将被忽略。如果你想 elementA
或 elementB
作为参考,你必须明确指定,例如auto&& elementA
或 auto& 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 elementA
,elementA
将是 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::string
。 auto
类型推导类似于模板类型推导,包括删除引用的 "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()
),你应该避免。