不同编译器中std容器的不同noexcept属性

时间:2017-12-28 16:29:38

标签: c++ std c++17

是否定义了容器实现的move-constructors 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 ++版本19.00.23506 for x64:

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 ++版本19.00.23506 for x64:

Vector Stack: 1
Vector Queue: 1

2 个答案:

答案 0 :(得分:2)

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

因此,他们转发他们正在适应的容器的noexcept行为。因此,如果移动deque,那么使用stack的{​​{1}}也是如此。实际上,容器适配器不会转发noexcept他们的组件容器的行为。或者至少,它们不是标准要求的。

至于实际容器的noexcept状态,deque需要vector移动(这是C ++ 17的更改;以前没有要求)。其余的是依赖于实现的。

答案 1 :(得分:2)

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

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

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