当 std::vector<T>
存储空间不足 emplace_back()
,它使用复制构造函数将元素复制到新存储中,如果 T
的移动构造函数不是 noexcept
.该程序打印 copy!
( godbolt ):
#include <vector>
#include <cstdio>
struct T {
T() = default;
T(T &&) { printf("move!\n"); }
T(const T &) { printf("copy!\n"); }
};
int main() {
std::vector<T> v;
v.emplace_back();
v.emplace_back();
}
如果我用 noexcept
标记移动构造函数, 这个程序打印 move!
.这一切都在意料之中。
现在,因为我不编写使用异常的代码,所以我使用 -fno-exceptions
关闭了异常。 .我预计我的示例(没有 noexcept
)将打印 move!
.但是,GCC 和 clang 仍然打印 copy!
.
这种行为是标准强制要求的吗?或者是否允许编译器使用 move
, 它只是没有针对这种情况进行优化?
最佳答案
标准没有说明关闭异常时会发生什么。 ISO C++ 是一种没有变体的单一语言,通过停用异常,您可以切换到标准不再涵盖的方言。
实现可以选择启用您要求的内容,但这样做需要库实现付出额外的努力。对于符合标准的实现,是否移动或复制的决定将基于 std::is_nothrow_move_constructible
,指定仅基于 noexcept
做出该决定移动构造函数签名中的说明符,而不是构造函数是否实际抛出异常。为了实现您的愿望,实现必须为 -fno-exceptions
实现不同的检测机制。方言作为非标准扩展。
标准中的相关段落是[vector.modifiers] :
template<class... Args> constexpr reference emplace_back(Args&&... args);
[...] If an exception is thrown while inserting a single element at the end and
T
is Cpp17CopyInsertable oris_nothrow_move_constructible_v<T>
istrue
, there are no effects.
尽管可能不是很明显,这会在 is_nothrow_move_constructible_v
时强制调用复制构造函数是错误的,因为这是实现符合此要求的唯一方法。请注意,在标准的早期版本中,事情更加微妙。 C++11 草案只说:
If an exception is thrown by the moveconstructor of a non-
CopyInsertable T
, the effects are unspecified.
它实际上强制执行与新措辞相同的行为。
现在,从理论上讲,如果一个实现除了寻找 noexcept
之外还有其他方法来检测函数是否可以抛出异常。 (例如,通过对移动构造函数的实现进行静态分析)是否允许在 C++11 措辞下移动?答案是肯定的,但我不知道有任何实现经过如此大的努力才能实现像这样的相当良性的优化。
关于c++ - 如果禁用异常,std::vector<T> 可以使用 T 的移动构造函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61417534/