如何将unique_ptr的队列添加到向量中的C++代码?

5

简化代码:

#include <queue>
#include <memory>
#include <vector>

class Foo {
public:
    Foo() {};
    virtual ~Foo() {}
};

int main()
{
    std::queue<std::unique_ptr<Foo>> queue;
    auto element = std::make_unique<Foo>();
    queue.push(std::move(element));
    std::vector<std::queue<std::unique_ptr<Foo>>> vector;
    // Error 1
    vector.push_back(queue); 
    // Error 2
    vector.push_back(std::move(queue));
    // Error 3
    vector.push_back({});
    return 0;
}

错误:

'std::unique_ptr>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': 尝试引用已删除的函数

显然,unique_ptr的复制构造函数被删除了,但我并没有尝试复制它。难道我真的在复制吗?


是的,push_back 会复制对象。要在原地创建对象,请使用 emplace_back - Ben Voigt
1
如果将std::queue替换为std::vector,它可以工作;但是在std::deque的默认队列底层下不起作用。但是对于不在向量中的deque进行move操作是可以正常工作的。奇怪。 - M.M
@M.M 是的,您可以移动一个不在 vector 中的 deque。但是该操作将不会是 noexcept 的。vector 只是不喜欢可能会抛出异常的移动(有关详细信息,请参见 aschepler 的答案)。 - Arne Vogel
1个回答

3
这有点棘手。如果以下两种情况之一成立,所有可以增加向量大小的 std::vector<T> 函数都必须以异常安全的方式执行:
  • T 具有保证永远不会抛出任何异常的移动构造函数;或者

  • T 具有复制构造函数。

因此,在大多数实现中,如果 T 具有声明为 nothrow 或等价的移动构造函数,则 vector 将使用这些操作的 T 的移动构造函数。如果没有,且 T 具有复制构造函数,则 vector 将使用复制构造函数,即使 T 具有移动构造函数。
问题在于,std::queue 总是声明它具有复制构造函数,即使该复制构造函数实际上无法实例化,并且总是声明它具有可能引发异常的移动构造函数,即使容器成员的移动构造函数保证不会抛出异常。
标准将这些指定为 [queue.defn]:
namespace std {
  template<class T, class Container = deque<T>>
  class queue {
    // ...
  public:
    explicit queue(const Container&);
    explicit queue(Container&& = Container());
    // ...
  };
}

这个类模板定义可以通过几种方式进行改进,使其更加" SFINAE 友好",避免像你遇到的这样的问题。(也许有人可以检查是否有其他类似问题并提交给库工作组一个提案。)

  1. Change the move constructor to promise not to throw if the Container type makes the same promise, typically done with language like:

    explicit queue(Container&& rhs = Container()) nothrow(see below);
    

    Remarks: The expression inside noexcept is equivalent to is_­nothrow_­move_­constructible_­v<Container>.

  2. Change the copy constructor to be deleted if the Container type is not copyable, typically done with language like:

    explicit queue(const Container&);
    

    Remarks: This constructor shall be defined as deleted unless is_­copy_­constructible_­v<Container> is true.


我发现直接使用deque也遇到了同样的问题。 - M.M
@M.M 不太确定你的意思。你能提供一个例子链接吗? - aschepler
我发现 这篇帖子 讨论了这个问题...涉及到分配器特性,这也解释了为什么我无法用一个测试用的非 nothrow_move_constructible 类(而不是 deque)重现这个问题。 - M.M
1
代码使用容器 queue< unique_ptr<Foo>, vector<unique_ptr<Foo>> > 构建正常,因此也许 deque 是潜在问题,而 queue 本身则没问题。 - M.M
谢谢大家。我可以将队列容器类型从deque更改为vector。换句话说,我正在将我的外部容器更改为std::vector<std::queue<std::unique_ptr<Foo>, std::vector<std::unique_ptr<Foo>>>> vector; - Mikhail Shatalin
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接