std::remove、std::move(范围)和移出的元素
std::remove, std::move(range) and moved-from elements
查看 std::remove 参考,我看到:
template< class ForwardIt, class T >
ForwardIt remove( ForwardIt first, ForwardIt last, const T& value );
删除是通过移动(通过移动赋值)范围内的元素来完成的。指向范围的新逻辑端和物理端之间元素的迭代器仍然是可解引用的,但元素本身具有未指定的值(根据 MoveAssignable post-条件) .
所以,我看到了 MoveAssignable 概念:
t = rv // Post-condition:The new value of rv is unspecified.
到目前为止,没问题。关于 MoveAssignable 概念的问题:
std::string s1 = "abc";
std::string s2;
s2 = std::move(s1);
std::cout << s1; // is this code valid?
s1 = "cde"; // is this code valid?
我可以通过读取它的值或重新分配它的值来重用 "moved from" 变量吗?
现在我正在查看 std::move 参考资料,它看起来有点令人惊讶:
template< class InputIt, class OutputIt >
OutputIt move( InputIt first, InputIt last, OutputIt d_first );
将范围 [first, last) 中的元素移动到从 d_first 开始的另一个范围。 此操作后,移出范围中的元素仍将包含适当类型的有效值,但不一定与移动前的值相同。
因此,std::remove 和 std::move 中移出元素的定义方式不同,尽管它们应该是相同的。哪一个是正确的?移出元素的重用(或不使用)规则是什么?
标准中的实际措辞可在 [lib.types.movedfrom] 部分找到:
Objects of types defined in the C++ standard library may be moved from ([class.copy]). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
std::move
([alg.move]) 的描述只是说元素被移动了,并没有对被移动的对象发表任何评论。 std::remove
([alg.remove])的描述有一个不规范的注释:
Note: each element in the range [ret,last)
, where ret
is the returned value, has a valid but unspecified state, because the algorithms can eliminate elements by moving from elements that were originally in that range.
所以 cppreference 恰好在这两个页面上使用了不同的措辞,但它们的意思完全相同。
现在,鉴于 std::string s1
已被移出,是否有效
std::cout << s1;
是的,它是允许的,因为 s1
是 "valid",但它没有用,因为您无法保证将输出什么。另一方面,
s1 = "cde";
既安全又有用,因为 s1
之前的所有内容都被丢弃了。
以"unless otherwise specified"为例,看一下std::unique_ptr<T>
的移动赋值运算符([unique.ptr.single.asgn]):
unique_ptr& operator=(unique_ptr&& u) noexcept;
Effects: Transfers ownership from u
to *this
as if by calling reset(u.release())
followed by get_deleter() = std::forward<D>(u.get_deleter())
.
由于 u.release()
离开 u
empty/null,我们实际上保证移出的对象将是 empty/null。移动构造函数和模板化构造函数以及采用具有不同模板参数的 unique_ptr
的赋值也有类似的保证,因此从 unique_ptr
移出的 unique_ptr
始终是 empty/null.
查看 std::remove 参考,我看到:
template< class ForwardIt, class T >
ForwardIt remove( ForwardIt first, ForwardIt last, const T& value );
删除是通过移动(通过移动赋值)范围内的元素来完成的。指向范围的新逻辑端和物理端之间元素的迭代器仍然是可解引用的,但元素本身具有未指定的值(根据 MoveAssignable post-条件) .
所以,我看到了 MoveAssignable 概念:
t = rv // Post-condition:The new value of rv is unspecified.
到目前为止,没问题。关于 MoveAssignable 概念的问题:
std::string s1 = "abc";
std::string s2;
s2 = std::move(s1);
std::cout << s1; // is this code valid?
s1 = "cde"; // is this code valid?
我可以通过读取它的值或重新分配它的值来重用 "moved from" 变量吗?
现在我正在查看 std::move 参考资料,它看起来有点令人惊讶:
template< class InputIt, class OutputIt >
OutputIt move( InputIt first, InputIt last, OutputIt d_first );
将范围 [first, last) 中的元素移动到从 d_first 开始的另一个范围。 此操作后,移出范围中的元素仍将包含适当类型的有效值,但不一定与移动前的值相同。
因此,std::remove 和 std::move 中移出元素的定义方式不同,尽管它们应该是相同的。哪一个是正确的?移出元素的重用(或不使用)规则是什么?
标准中的实际措辞可在 [lib.types.movedfrom] 部分找到:
Objects of types defined in the C++ standard library may be moved from ([class.copy]). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
std::move
([alg.move]) 的描述只是说元素被移动了,并没有对被移动的对象发表任何评论。 std::remove
([alg.remove])的描述有一个不规范的注释:
Note: each element in the range
[ret,last)
, whereret
is the returned value, has a valid but unspecified state, because the algorithms can eliminate elements by moving from elements that were originally in that range.
所以 cppreference 恰好在这两个页面上使用了不同的措辞,但它们的意思完全相同。
现在,鉴于 std::string s1
已被移出,是否有效
std::cout << s1;
是的,它是允许的,因为 s1
是 "valid",但它没有用,因为您无法保证将输出什么。另一方面,
s1 = "cde";
既安全又有用,因为 s1
之前的所有内容都被丢弃了。
以"unless otherwise specified"为例,看一下std::unique_ptr<T>
的移动赋值运算符([unique.ptr.single.asgn]):
unique_ptr& operator=(unique_ptr&& u) noexcept;
Effects: Transfers ownership from
u
to*this
as if by callingreset(u.release())
followed byget_deleter() = std::forward<D>(u.get_deleter())
.
由于 u.release()
离开 u
empty/null,我们实际上保证移出的对象将是 empty/null。移动构造函数和模板化构造函数以及采用具有不同模板参数的 unique_ptr
的赋值也有类似的保证,因此从 unique_ptr
移出的 unique_ptr
始终是 empty/null.