C++ 如何将unique_ptr队列添加到向量中

C++ How to add queue of unique_ptr's to a vector

提问人:Mikhail Shatalin 提问时间:6/5/2018 最后编辑:M.MMikhail Shatalin 更新时间:6/5/2018 访问量:687

问:

简化代码:

#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::d efault_delete<_Ty>> &)':尝试 引用已删除的函数

显然,复制 unique_ptr 的 c~tor 被删除了,但我不是要复制它。我是吗?

C++ STL 复制构造函数 unique-ptr

评论

0赞 Ben Voigt 6/5/2018
是的,复制一份。要就地创建对象,请使用 。push_backemplace_back
1赞 M.M 6/5/2018
如果替换为 ;不适用于 的默认队列底层。但它对不在向量中的一个确实工作正常。奇怪std::queuestd::vectorstd::dequemovedeque
0赞 Arne Vogel 6/5/2018
@M.M 是的,您可以移动不在 .但是,该操作不会。 只是不喜欢潜在的投掷动作(有关详细信息,请参阅 Aschepler 的回答)。dequevectornoexceptvector

答:

3赞 aschepler 6/5/2018 #1

这有点棘手。如果以下两种情况之一为真,则所有可以增加向量大小的函数都必须以异常安全的方式执行:std::vector<T>

  • T有一个移动构造函数,可以保证它永远不会抛出任何异常;或

  • T具有复制构造函数。

因此,在大多数实现中,如果声明了移动构造函数或等效函数,则将使用移动构造函数进行这些操作。如果没有,并且具有复制构造函数,则将使用复制构造函数,即使具有移动构造函数也是如此。TnothrowvectorTTvectorT

这里的问题是,它总是声明它有一个复制构造函数,即使该复制构造函数实际上不能实例化,并且总是声明它有一个可能会抛出的移动构造函数,即使容器成员的移动构造函数保证它不会抛出。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. 将移动构造函数更改为承诺在类型做出相同承诺时不抛出,通常使用如下语言完成:Container

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

    言论:里面的表达式等效于 。noexceptis_­nothrow_­move_­constructible_­v<Container>

  2. 如果类型不可复制,则更改要删除的复制构造函数,通常使用以下语言完成:Container

    explicit queue(const Container&);
    

    言论:此构造函数应定义为已删除,除非是 。is_­copy_­constructible_­v<Container>true

评论

0赞 M.M 6/5/2018
我发现直接使用也有同样的问题deque
0赞 aschepler 6/5/2018
@M.M 不明白你的意思。你能链接到一个例子吗?
0赞 aschepler 6/5/2018
@M.M:好的,是的。的移动构造函数也未声明。这更令人惊讶,因为无论元素类型是什么,都应该可以移动 a,而这与没有明显原因不同。queuenoexceptdequevector
0赞 M.M 6/5/2018
我发现这篇文章在谈论它......涉及分配器特征,这就解释了为什么我无法用测试非nothrow_move_constructible类重现该问题(而不是deque)
1赞 M.M 6/5/2018
代码使用容器构建正常,因此可能是潜在的问题,并且本身没问题queue< unique_ptr<Foo>, vector<unique_ptr<Foo>> >dequequeue