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

时间:2018-06-05 01:10:47

标签: c++ stl copy-constructor unique-ptr

简化代码:

#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;
}

错误:

  

&#39;的std ::的unique_ptr&GT; ::的unique_ptr(常量   的std ::的unique_ptr&LT; _Ty,性病:: default_delete&LT; _Ty&GT;&GT; &amp;)&#39;:尝试   引用已删除的功能

显然删除了unique_ptr的复制文件,但我没有尝试复制它。我呢?

1 个答案:

答案 0 :(得分: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&#34;并避免像你遇到的问题。 (也许有人可以检查其他类似问题的课程并向图书馆工作组提交建议。)

  1. 如果Container类型具有相同的承诺,则更改移动构造函数以保证不抛出,通常使用以下语言完成:

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

    备注: noexcept内的表达式相当于is_­nothrow_­move_­constructible_­v<Container>

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

    explicit queue(const Container&);
    

    备注:除非is_­copy_­constructible_­v<Container>true,否则此构造函数应定义为已删除。