如何将std :: mutex放在std :: tuple里面和其他一些类一起?

时间:2016-04-05 13:55:58

标签: c++ c++11

我想std::shared_ptr<std::tuple<MyClass, std::mutex>>

std::mutex既不可复制也不可移动。如何初始化?

1 个答案:

答案 0 :(得分:1)

两个参数的简单解决方案是std::pairstd::piecewise_construct

std::tuple遗漏了这一点。我认为这是一个缺陷。

如果我们想要tuple(比如超过2个参数),我们可以用一些可怕的代码来做,因为“可以做到”的数值有限。

template<class T>
struct placement_shared_helper {
  std::aligned_storage_t<sizeof(T),alignof(T)> data;
  T* get() { return reinterpret_cast<T*>(&data); }
  template<class F>
  void construct( F&& f ) {
    std::forward<F>(f)((void*)&data);
  }
  // dangerous if construct throws:
  ~placement_shared_helper(){ get()->~T(); }
};

template<class T, class F>
std::shared_ptr<T> make_placement_shared( F&& f ) {
  auto r1 = std::make_shared<placement_shared_helper<T>>();
  r1->construct( std::forward<F>(f) );
  return {r1->get(), r1}; // aliasing constructor
}

这要求你传入一个函数对象,当传递一个足够大小的void*并且对齐时,它将在指向的存储中构造你的类型。

您可以提高placement_shared_helper的异常安全性,但这是有效的,我很懒。考虑从构造和std::terminate中捕获任何异常。

下一部分,我们作弊。这违反了标准,因为我们采用未初始化的内存并将其解释为元组以获取元组存储其对象的偏移量。糟糕的程序员。

template<class Dest, std::size_t...Is, class...Args>
void placement_construct( std::index_sequence<Is...>, Dest* here, std::tuple<Args...> args ) {
  new((void*)here) Dest( std::get<Is>(args)... );
}
template<class Dest, class...Args>
void placement_construct( Dest* here, std::tuple<Args...> args ) {
  return placement_construct( std::index_sequence_for<Args...>{}, here, std::move(args) );
}

template<class Dest, std::size_t...Is, class...Tuples>
void placement_construct_tuple( std::index_sequence<Is...>, void* here, Tuples... tuples ) {
  using discard=int[];
  (void)discard{0,(void(
      placement_construct( &std::get<Is>(*(Dest*)here), std::move(tuples) )
  ),0)...};
}

template<class Dest, class...Tuples>
void placement_construct_tuple( void* here, Tuples... tuples ) {
  placement_construct_tuple<Dest>( std::index_sequence_for<Tuples...>{}, here, std::move(tuples)... );
}

这假定元组只是特定位置的一堆构造对象。我们采用元组形状的内存块并依次构造其每个元素。如果抛出异常就会发生坏事;你可能想尝试捕捉终止。

make_placement_shared采用一个void*的函数来构造你的对象。

placement_construct_tuple采用一组元组,并使用它们在元组中依次构造对象。

auto r = make_placement_shared<std::tuple<MyClass, std::mutex>>(
  [&](void* here) {
    placement_construct_tuple<std::tuple<MyClass, std::mutex>>(here,
      std::forward_as_tuple( args_to_construct_MyClass_go_here ),
      std::make_tuple() // no args for mutex
    );
  }
);

并禁止打字错误和未定义的行为,已完成。