C++ 为什么不';t容器移动分配运算符不例外?
我注意到C++ 为什么不';t容器移动分配运算符不例外?,c++,c++11,C++,C++11,我注意到std::string(实际上std::basic_string)的移动赋值运算符是noexcept。这对我来说是有道理的。但随后我注意到,没有一个标准容器(例如,std::vector,std::deque,std::list,std::map)声明其移动分配操作符noexcept)。这对我来说没什么意义。例如,std::vector,通常实现为三个指针,指针当然可以在不引发异常的情况下进行移动分配。然后我想可能问题在于移动容器的分配器,但是std::string也有分配器,所以如果这
std::string
(实际上std::basic_string
)的移动赋值运算符是noexcept
。这对我来说是有道理的。但随后我注意到,没有一个标准容器(例如,std::vector
,std::deque
,std::list
,std::map
)声明其移动分配操作符noexcept
)。这对我来说没什么意义。例如,std::vector
,通常实现为三个指针,指针当然可以在不引发异常的情况下进行移动分配。然后我想可能问题在于移动容器的分配器,但是std::string
也有分配器,所以如果这是问题所在,我希望它会影响std::string
那么为什么
std::string
的移动分配操作符noexcept
,而标准容器的移动分配操作符却不是呢?我认为其原因是这样的
basic_string
仅适用于非数组POD类型。因此,它们的析构函数必须是微不足道的。这意味着,如果您对move赋值执行交换
,则moved to字符串的原始内容尚未销毁对您来说并不重要
<> p>容器(因此,这种差异是有意义的。一旦开始释放内存(通过分配器),就不能进行移动分配
noexcept
,因为这可能会通过异常失败。因此,一旦您开始要求在move assign上释放内存,您就无法强制执行noexcept
我相信我们看到的是标准缺陷。如果要将noexcept
规范应用于移动分配操作符,则该规范有点复杂。我相信无论我们谈论的是basic\u string
还是vector
,这句话都是正确的
根据[container.requirements.general]/p7,我对集装箱移动分配操作员应该做的工作的英文翻译是:
C& operator=(C&& c)
如果alloc\u traits::propagate\u on\u container\u move\u assignment::value
为
true
、转储资源、移动分配程序和传输
来自c
的资源
如果
alloc\u traits::在容器上传播\u移动\u赋值::value
为false
和get_allocator()==c.get_allocator()
,转储资源和传输
来自c
的资源
如果
alloc\u traits::在容器上传播\u移动\u赋值::value
为false
和get_分配器()!=c、 get_allocator()
,move分配每个c[i]
注:
alloc\u traits
指的是alloctor\u traits
这项调查已经进行了大约一年,但据我所知仍然有效。容器类中的移动分配操作符被定义为无异常,因为许多容器都是为实现强异常安全保证而设计的。容器实现了强大的异常安全保证,因为在有移动分配操作符之前,必须复制容器。如果副本出现任何问题,新存储将被删除,容器将保持不变。现在我们被这种行为困住了。如果移动任务OP不在,则调用较慢的复制赋值操作符。你到底在哪里看到这个?@ C++标准中的洛基斯塔里。OP在问为什么它不是noexcept
@nicolasbolas idiot me:),我反过来问了关于运算符=for string&。按标准,基本字符串不是容器。基本字符串的运算符=实际上并不例外<代码>效果:如果*this和str不是同一个对象,则修改*this,如表71所示。[注意:有效的实现是交换(str)。-结束注意]
解除分配永远不会失败。如果释放资源可能会出现异常,那么几乎不可能编写容错C++代码。取消分配也发生在容器自己的析构函数(也将调用用户定义类型的析构函数)中,但C++11中的析构函数默认为noexcept
,这是有充分理由的。@AdamH.Peterson:析构函数默认为noexcept,但分配器不是。值得注意的是,.@nicolas,这可能是真的,但是标准库的容器的析构函数是noexcept
,这些析构函数调用分配器上的释放。如果分配器的释放函数失败,您最终会看到程序因noexcept冲突而中止。这对我来说意味着move()
的原因可能不是noexcept
与解除分配失败无关。为什么它应该应用于basic_string
?@nicolas:[container.requirements.general]/p13:本条款和(21.4)中定义的所有容器如表99所述,数组满足分配器感知容器的附加要求。如果总是相等,则可以通过使用来改善条件(C++17)。顺便说一句,您可能想使用分配器特性专门化,而不是原始分配器类型。
C& operator=(C&& c)
noexcept(
alloc_traits::propagate_on_container_move_assignment::value &&
is_nothrow_move_assignable<allocator_type>::value);
C& operator=(C&& c)
noexcept(
alloc_traits::propagate_on_container_move_assignment{} ||
alloc_traits::is_always_equal{});