尝试将 stack<unique_ptr> (或 deque)推入向量时,“结果类型必须可从输入范围的值类型构造”

bea*_*605 5 c++

我基本上有这样的东西(我更改了变量名称,抱歉,如果看起来很奇怪)

#include <stack>
#include <memory>
#include <vector>

struct Sequence {
    std::stack<std::unique_ptr<int>> numbers;
    // It works if I change it to this
    //std::stack<std::unique_ptr<int>, std::vector<std::unique_ptr<int>>> numbers; 
    Sequence(const std::vector<int> &v) {
        for (int i : v) {
            numbers.push(std::make_unique<int>(i));
        }
    }
};

int main() {
    // constructing alone works fine
    // Sequence s{{3, 1, 5}};   

    // but pushing it into a vector gives an error
    std::vector<Sequence> ts;
    ts.emplace_back(std::vector<int>{1, 5, 2});
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译出现这个错误:

/usr/include/c++/10/bits/stl_uninitialized.h:137:72: error: static assertion failed: result type must be constructible from value type of input range
  137 |       static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
Run Code Online (Sandbox Code Playgroud)

当我尝试将序列放入向量中时。但是,如果我将底层堆栈容器从 更改为std::dequestd::vector它就可以工作。我的猜测与移动和复制有关unique_ptr,但我不太明白其中的细微差别——有人知道发生了什么吗?

Bri*_*ian 11

这似乎是旧的“move if noexcept”问题的一个实例std::vector。简而言之,std::vector<Sequence>::push_back可能需要重新分配,在这种情况下会使用 的复制构造函数,Sequence因为它无法证明移动构造函数不会抛出异常。为了强制它使用移动构造函数,您必须将 的复制构造函数标记为Sequence已删除:

Sequence(const Sequence&) = delete;
Sequence(Sequence&&) = default;
Run Code Online (Sandbox Code Playgroud)

这应该能让你的代码编译。

当堆栈的底层容器发生更改时,问题消失的原因是std::vectornoexcept 是可移动的,而std::deque可能不是(并且,在您似乎正在使用的 libstdc++ 中,它不是)。此 noexcept-movability 属性由 的包含std::stack和直至隐式移动构造函数传播Sequence。因此,对于底层vector, 会将Sequence自己宣传为 noexcept-movable,并且封闭vector会做正确的事情。对于底层双端队列, 会将Sequence自身宣传为 not-noexcept-movable,封闭vector实例化复制构造函数,并且实例化失败,因为它需要 a 的复制构造函数unique_ptr

  • @TedLyngmo 确实如此。它会导致隐式复制构造函数的删除。这条规则很晦涩,因此依赖它可能会让下一个阅读代码的人(可能是他自己)感到困惑。 (2认同)