如何构造具有唯一指针的向量

Eck*_*rdN 6 c++ initializer-list unique-ptr c++11 list-initialization

我尝试用unique_ptr 构造一个向量。但是我没有找到直接的方法。以下代码无法编译。错误是:调用 'std::__1::unique_ptr >' 的隐式删除复制构造函数:

#include <iostream>
#include <memory>
#include <utility>
#include <vector>
class test1{
public:
    test1(){};
    test1(test1&&)=default;
};

int main(int argc, const char * argv[]) {
    std::unique_ptr<test1> us(new test1());
    std::vector<std::unique_ptr<test1>> vec{move(us)};
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Yak*_*ont 6

make_vector是一个接受任意数量的参数并将它们完美转发到向量中的函数。

// get the first type in a pack, if it exists:
template<class...Ts>
struct first {};
template<class T, class...Ts>
struct first<T,Ts...>{
  using type=T;
};
template<class...Ts>
using first_t=typename first<Ts...>::type;

// build the return type:
template<class T0, class...Ts>
using vector_T = 
  typename std::conditional<
    std::is_same<T0, void>::value,
    typename std::decay<first_t<Ts...>>::type,
    T0
  >::type;
template<class T0, class...Ts>
using vector_t = std::vector< vector_T<T0, Ts...> >;

// make a vector, non-empty arg case:
template<class T0=void, class...Ts, class R=vector_t<T0, Ts...>>
R make_vector( Ts&&...ts ) {
  R retval;
  retval.reserve(sizeof...(Ts)); // we know how many elements
  // array unpacking trick:
  using discard = int[];
  (void)discard{0,((
    retval.emplace_back( std::forward<Ts>(ts) )
  ),void(),0)...};
  return retval; // NRVO!
}
// the empty overload:
template<class T>
std::vector<T> make_vector() {
  return {};
}
Run Code Online (Sandbox Code Playgroud)

使用:

std::vector<std::unique_ptr<test1>> vec =
  make_vector(
    std::move(u1), std::move(u2)
  );
Run Code Online (Sandbox Code Playgroud)

活生生的例子

我稍微打磨了一下。如果您向其传递 1 个或多个参数并且不向其传递类型,它将推断出返回类型。如果您向它传递一个类型,它将使用该类型。如果你未能向它传递类型或任何参数,它会抱怨。(如果您转发包,或者将其存储在特定类型中,我总是给它一个类型)。


可以采取进一步的步骤,我们进行返回类型推导,以消除指定类型的要求,即使在空情况下也是如此。我不知道,这在您的用例中可能是必需的,但它与您不需要指定 a 类型的方式相匹配{},所以我想我应该把它扔掉:

template<class...Ts>
struct make_vec_later {
  std::tuple<Ts...> args; // could make this `Ts&&...`, but that is scary

   // make this && in C++14
  template<class T, size_t...Is>
  std::vector<T> get(std::index_sequence<Is...>) {
    return make_vector<T>(std::get<Is>(std::move(args))...);
  }

  // make this && in C++14
  template<class T>
  operator std::vector<T>(){
    return std::move(*this).template get<T>( std::index_sequence_for<Ts...>{} );
  }
};
template<class...Ts>
make_vec_later<Ts...> v(Ts&&...ts) {
  return {std::tuple<Ts...>(std::forward<Ts>(ts)...)};
}
Run Code Online (Sandbox Code Playgroud)

这确实依赖于 的 C++14 功能index_sequence,但如果您的编译器还没有,那么很容易用 C++11 重写它们。只需在堆栈溢出上谷歌一下,就有无数的实现。

现在语法如下:

std::vector<std::unique_ptr<test1>> vec =
  v(std::move(u1));
Run Code Online (Sandbox Code Playgroud)

其中参数列表可以为空。

实例

支持变体分配器留给用户作为练习。将另一种类型添加到make_vectorCalled中A,并将其默认为void. 如果它为空,请将其交换为为向量选择的std::allocator<T>任何类型。T在返回类型推导版本中,执行类似的操作。


Pra*_*ian 5

您正在调用带有参数的vector构造函数(链接页面上的(7))initializer_list<T>。Aninitializer_list 只允许const访问其元素,因此vector必须复制元素,这当然无法编译。

以下应该工作

std::unique_ptr<test1> us(new test1());
std::vector<std::unique_ptr<test1>> vec;

vec.push_back(move(us));
// or
vec.push_back(std::unique_ptr<test1>(new test1()));
// or
vec.push_back(std::make_unique<test1>()); // make_unique requires C++14
Run Code Online (Sandbox Code Playgroud)

您可以使用vector带有两个迭代器的构造函数,但解决方案仍然不是单行的,因为它需要您定义一个临时数组,然后您可以从中移动。

std::unique_ptr<test1> arr[] = {std::make_unique<test1>()};
std::vector<std::unique_ptr<test1>> vec{std::make_move_iterator(std::begin(arr)),
                                        std::make_move_iterator(std::end(arr))};
Run Code Online (Sandbox Code Playgroud)

  • @0x499602D2 不是异常安全的,如果 `emplace_back` 抛出,你会泄漏。\ (2认同)
  • @0x499602D2 那么我的例子是什么?在第二种情况下,`unique_ptr` 构造保证在调用 `push_back` 之前完成。`push_back(make_shared&lt;test1&gt;())` 不会编译,我没有提到 `push_back(make_unique&lt;test1&gt;())` 因为这个问题被标记为 C++11,但无论如何我都会添加. (2认同)