为什么 std::vector 允许对其包含的类型使用可抛出的移动构造函数?
Why does std::vector allow the use of a throwable move constructor for the type it contains?
显然,std::move_if_noexcept()
会调用移动构造函数,即使它没有标记为noexcept
如果没有可用的复制构造函数。
从 cpprefeerence.com(强调我的):
笔记
例如,
std::vector::resize
使用此方法,可能必须分配新存储,然后将元素从旧存储移动或复制到新存储。如果在此操作期间发生异常,std::vector::resize
撤消到目前为止所做的一切,这只有在使用std::move_if_noexcept
来决定是使用移动构造还是复制构造时才有可能。(除非复制构造函数不可用,在这种情况下,无论哪种方式都使用移动构造函数,并且可以免除强异常保证)
由于std::vector
在重新分配时使用此函数,这可能会使向量和应用程序处于不确定状态。 那么,为什么会允许这样做呢?
假设您正在使用move_if_noexcept
时做vector
正在做的事情。也就是说,您有一些对象obj
,并且您需要从obj
构造该类型的新值。之后,您将删除obj
.这是移动对象的主要情况,因此vector
尽可能这样做。
如果移动是noexcept
的,则根据定义,移动obj
是异常安全的。如果不是noexcept
,那么你需要问:如果移动构造函数抛出会发生什么?在这种情况下,obj
的状态如何?答案是...你不知道。更糟糕的是,您已经成功移动的任何对象的状态如何?你能把它们移回来吗?
但是,当涉及到复制构造函数时,您确实知道。复制构造函数将const&
带到源对象。因此,根据定义,失败的复制操作无法修改obj
(是的,我们知道您可以const_cast
,但这会使您的复制构造函数成为谎言。像这样的谎言是auto_ptr
不再存在的原因,我猜在标准中全面禁止撒谎的副本构造函数)。因此,在失败的副本上,obj
处于其原始状态。
因此,如果移动可以抛出,则首选复制,因为这提供了强大的异常保证:如果发生异常,一切都会恢复
原样。但是,如果你唯一的选择是投掷动作,那么你有两个选择:使这种类型永远不能与vector
一起使用,或者提供任何例外保证类型本身在移动失败时提供。后者是被选择的。
这是允许的,因为这是你要求的。您选择的类型不允许强异常保证,因此无法提供。但是您仍然可以制作此类类型的vector
;您只需要处理不可复制的移动失败的可能性。
由于你是那种使用无法提供强大异常保证的类型的人,你显然必须知道如何处理这些场景,对吧?
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- std::vector::p ush_back() 不会在 MSVC 上编译具有已删除移动构造函数的对象
- 仅包含可移动 std::map 的类的移动构造函数不起作用
- 为什么调用复制构造函数而不是移动构造函数?
- 基类中的默认析构函数禁用子类中的移动构造函数(如果有成员)
- 从具有按值捕获的 lambda 移动构造 std::函数时,移动构造函数调用两次
- 移动构造函数和右值引用
- 为什么 std::memmove 中联合的默认非平凡移动构造函数C++?
- 具有专用化的模板类中的可靠条件复制和移动构造函数
- C++:为什么不调用移动构造函数?
- 移动构造函数永远不会被调用
- C++:关于使用 Stroustrup 示例移动构造函数/赋值的问题
- 运算符+ 的规范实现涉及额外的移动构造函数
- C ++为什么在移动构造函数中需要移动/前进
- 为什么在删除"移动构造函数"时使用"复制构造函数"?
- 为什么这里不调用移动构造函数?
- 隐式移动构造函数
- 如何为具有私有成员的派生类实现移动构造函数
- 为什么不调用移动构造函数
- 是否可以避免在以下代码中复制/移动构造函数的需要?