矢量<unique_ptr<A>>使用初始化列表

2021-12-30 00:00:00 constructor containers c++ c++11 unique-ptr

我遇到了一个错误:在编译类似于下面的代码时调用'std::__1::unique_ptr>'的隐式删除的复制构造函数c++ -std=c++14 unique_ptr_vector.cpp -o main

这是一个简化版本:

头文件'my_header.h':

header file 'my_header.h':

#include <iostream>
#include <string>
#include <memory>
#include <vector>

class A{
public:
    A() : n(0) {}
    A(int val) : n(val) {} 
    A(const A &rhs): n(rhs.n) {}
    A(A &&rhs) : n(std::move(rhs.n)) {}
    A& operator=(const A &rhs) { n = rhs.n; return *this; }
    A& operator=(A &&rhs) { n = std::move(rhs.n); return *this; }
    ~A() {}

    void print() const { std::cout << "class A: " << n << std::endl; }
private:
    int n;
};

namespace {
    std::vector<std::unique_ptr<A>> vecA = {
        std::make_unique<A>(1),
        std::make_unique<A>(2),
        std::make_unique<A>(3),
        std::make_unique<A>(4)
    };
}

还有我的 src 文件 unique_ptr_vector.cpp:

And my src file unique_ptr_vector.cpp:

#include "my_header.h"

using namespace std;

int main()
{
    for(const auto &ptrA : vecA){
        ptrA->print();
    }
    return 0;
}

我真的需要为每个组件单独使用 push_back(std::make_unique()),或者在标题中填充容器的首选方法是什么?或者这是一个坏主意?

Do I really need to use push_back(std::make_unique<A>(<some_number>)) individually for each component, Or what would be a preferred way to populate a container in a header? Or is this a bad idea in general?

我看到了诸如 这个,这个和这个.

我现在知道初始化列表似乎是不可能的.但是人们通常用 container 做什么.我是否应该简单地避免在标题中对其进行初始化...

I know now Initialization list seems impossible. but what do people normally do with container<unique_ptr>. Should I just simply avoid initialize that in a header...

推荐答案

初始化列表是 const 数组的包装器.

Initialization lists are wrappers around const arrays.

unique_ptrconst 不能移动.

我们可以像这样(以完全合法的方式)解决这个问题:

We can hack around this (in a perfectly legal way) like this:

template<class T>
struct movable_il {
  mutable T t;
  operator T() const&& { return std::move(t); }
  movable_il( T&& in ): t(std::move(in)) {}
};

template<class T, class A=std::allocator<T>>
std::vector<T,A> vector_from_il( std::initializer_list< movable_il<T> > il ) {
  std::vector<T,A> r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
  return r;
}

实例.

使用:

auto v = vector_from_il< std::unique_ptr<int> >({
  std::make_unique<int>(7), 
  std::make_unique<int>(3)
});

如果你想知道为什么初始化器列出引用常量数据,你必须追踪并阅读委员会会议记录或询问当时在场的人.我猜这是关于最小惊讶原则和/或对可变数据和视图类型有缺陷的人(例如将 array_view 重命名为 span).

If you want to know why initializer lists reference const data, you'll have to track down and read committee minutes or ask someone who was there. I'd guess it is about the principle of least surprise and/or people with bugaboos about mutable data and view types (such as the renaming of array_view to span).

如果你想要的不仅仅是向量:

If you want more than just vectors:

template<class C, class T=typename C::value_type>
C container_from_il( std::initializer_list< movable_il<T> > il ) {
  C r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
  return r;
}

仍然需要按摩才能与关联容器一起工作,因为我们还想移动键.

which still needs massaging to work right with associative containers as we also want to move the key.

template<class VT>
struct fix_vt {
  using type=VT;
};
template<class VT>
using fix_vt_t = typename fix_vt<VT>::type;
template<class VT>
struct fix_vt<const VT>:fix_vt<VT>{};
template<class K, class V>
struct fix_vt<std::pair<K,V>>{
  using type=std::pair<
    typename std::remove_cv<K>::type,
    typename std::remove_cv<V>::type
  >;
};

template<class C, class T=fix_vt_t<typename C::value_type>>
C container_from_il( std::initializer_list< movable_il<T> > il ) {
  C r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
  return r;
}

相关文章