gpt4 book ai didi

c++ - 为什么容器移动赋值运算符不是 noexcept?

转载 作者:IT老高 更新时间:2023-10-28 12:31:18 25 4
gpt4 key购买 nike

我注意到 std::string 的(实际上是 std::basic_string 的)移动赋值运算符是 noexcept。这对我来说很有意义。但后来我注意到没有一个标准容器(例如,std::vectorstd::dequestd::liststd::map) 声明其移动赋值运算符 noexcept。这对我来说意义不大。例如,std::vector 通常实现为三个指针,并且指针当然可以移动分配而不会引发异常。然后我认为问题可能在于移动容器的分配器,但是 std::string 也有分配器,所以如果这是问题,我希望它会影响 std::string.

那么为什么std::string的移动赋值运算符是noexcept,而标准容器的移动赋值运算符却不是?

最佳答案

我相信我们正在研究一个标准缺陷。 noexcept规范,如果要应用于移动赋值运算符,则有些复杂。无论我们谈论的是basic_string,我都相信这个说法是正确的。或 vector .

基于 [container.requirements.general]/p7 我对容器移动赋值运算符应该做的事情的英文翻译是:

C& operator=(C&& c)

If alloc_traits::propagate_on_container_move_assignment::value is true, dumps resources, move assigns allocators, and transfers resources from c.

If alloc_traits::propagate_on_container_move_assignment::value is false and get_allocator() == c.get_allocator(), dumps resources, and transfers resources from c.

If alloc_traits::propagate_on_container_move_assignment::value is false and get_allocator() != c.get_allocator(), move assigns each c[i].

注意事项:

  1. alloc_traitsallocator_traits<allocator_type> .

  2. alloc_traits::propagate_on_container_move_assignment::valuetrue可以指定移动赋值运算符noexcept因为它要做的就是释放当前资源,然后从源头窃取资源。同样在这种情况下,分配器也必须被移动分配,并且移动分配必须是 noexcept容器的移动分配为 noexcept .

  3. alloc_traits::propagate_on_container_move_assignment::valuefalse ,并且如果两个分配器相等,那么它将执行与#2 相同的操作。但是,直到运行时才知道分配器是否相等,因此您不能基于 noexcept关于这种可能性。

  4. alloc_traits::propagate_on_container_move_assignment::valuefalse ,并且如果两个分配器相等,则必须移动分配每个单独的元素。这可能涉及向目标添加容量或节点,因此本质上是 noexcept(false) .

总之:

C& operator=(C&& c)
noexcept(
alloc_traits::propagate_on_container_move_assignment::value &&
is_nothrow_move_assignable<allocator_type>::value);

而且我看不到对 C::value_type 的依赖在上面的规范中,所以我相信它应该同样适用于 std::basic_string尽管 C++11 另有说明。

更新

在下面的评论中,哥伦布正确地指出,事情一直在逐渐发生变化。我上面的评论是相对于 C++11 的。

对于 C++17 草案(在这一点上看起来很稳定),情况发生了一些变化:

  1. 如果 alloc_traits::propagate_on_container_move_assignment::valuetrue ,规范现在需要 allocator_type 的移动分配不抛出异常 (17.6.3.5 [allocator.requirements]/p4)。所以不再需要检查is_nothrow_move_assignable<allocator_type>::value .

  2. alloc_traits::is_always_equal已添加。如果这是真的,那么可以在编译时确定上面第 3 点不能抛出,因为可以转移资源。

所以新的 noexcept容器规范可以是:

C& operator=(C&& c)
noexcept(
alloc_traits::propagate_on_container_move_assignment{} ||
alloc_traits::is_always_equal{});

而且,对于 std::allocator<T> , alloc_traits::propagate_on_container_move_assignment{}alloc_traits::is_always_equal{}都是真的。

现在也在 C++17 草案中,vectorstring移动赋值准确这个noexcept规范。然而,其他容器带有此 noexcept 的变体规范。

如果您关心这个问题,最安全的做法是测试您关心的容器的显式特化。我已经为 container<T> 做到了这一点。对于 VS,libstdc++ 和 libc++ 在这里:

http://howardhinnant.github.io/container_summary.html

这项调查大约有一年的历史,但据我所知仍然有效。

关于c++ - 为什么容器移动赋值运算符不是 noexcept?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12332772/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com