不同编译器中标准容器的不同 noexcept 属性

Different noexcept property for std containers in different Compilers

是否定义了容器实现的移动构造函数noexcept属性?我刚刚发现以下内容在 clang 中有效,但在 gcc 或 msvc++ 中无效:

std::vector<std::vector<std::unique_ptr<int>>> vector_a;
std::vector<std::stack<std::unique_ptr<int>>> vector_b;
vector_a.reserve(10);  // this works in all tested compilers
vector_b.reserve(10);  // this only works in clang

我的问题是,这是由于标准实施不完整造成的,还是根本没有定义(故意?)。

我测试了一些标准容器:

#include <iostream>
#include <deque>
#include <vector>
#include <queue>
#include <stack>

int main() {
    std::cout << "Deque: " << std::is_nothrow_move_constructible<std::deque<float>>::value << std::endl;
    std::cout << "Vector: " << std::is_nothrow_move_constructible<std::vector<float>>::value << std::endl;
    std::cout << "Queue: " << std::is_nothrow_move_constructible<std::queue<float>>::value << std::endl;
    std::cout << "Stack: " << std::is_nothrow_move_constructible<std::stack<float>>::value << std::endl;
}

gcc 7.2.1:

Deque: 0
Vector: 1
Queue: 0
Stack: 0

clang 5.0.0:

Deque: 1
Vector: 1
Queue: 1
Stack: 1

Microsoft C/C++ x64 版本 19.00.23506:

Deque: 0
Vector: 1
Queue: 0
Stack: 0

编辑

使用向量作为底层容器的队列和堆栈的结果:

std::cout << "Vector Stack: " << std::is_nothrow_move_constructible<std::stack<float, std::vector<float>>>::value << std::endl;
std::cout << "Vector Queue: " << std::is_nothrow_move_constructible<std::queue<float, std::vector<float>>>::value << std::endl;

gcc 7.2.1:

Vector Stack: 1
Vector Queue: 1

clang 5.0.0:

Vector Stack: 1
Vector Queue: 1

Microsoft C/C++ x64 版本 19.00.23506:

Vector Stack: 1
Vector Queue: 1

stackqueue 不是容器;它们是容器 适配器 。他们使用 你给他们的容器类型作为模板参数。默认情况下,他们使用 std::deque.

因此,他们转发他们正在适配的容器的 noexcept 行为。因此,如果 deque 在移动时抛出,那么使用 deque.stack 也会抛出。实际上,容器适配器 不会 转发其组件容器的 noexcept 行为。或者至少,标准不需要它们。

至于实际容器的 noexcept 状态,vector 需要进行 noexcept 移动(这是 C++17 的更改;之前没有要求)。其余的取决于实现。

只保证 vector 的移动构造函数 noexcept 并且仅从 C++17 开始。

deque, stack and queue 移动构造函数不是 noexcept。 LLVM 的实现是 noexcept 这一事实是一个很好的补充,它与标准不矛盾。

您可能想查看 deque::swap(),它提供了类似的功能并且是 noexcept(也是自 C++17 起)。