如何在 boost::variant 包裹的指针容器中将原始指针更改为 unique_ptr

How to change raw pointer to unique_ptr in container of pointers wrapped by boost::variant

Live code example

我正在尝试在向量中保存指向基 class 模板化版本的指针变体。指针的 boost::variant 恰好包含在一个结构中。如果这些指针是原始指针,它工作正常,但是当我将它们更改为 unique_ptr.

时,事情开始出错
struct Sayer {

  struct Category {
    using GetterVariant = boost::variant<
      //Getter<string>*, // works OK
      //Getter<double>*, // ...
      //Getter<int>*     // ...
      unique_ptr<Getter<string>>,
      unique_ptr<Getter<double>>,
      unique_ptr<Getter<int>>
    >;
    Category(GetterVariant g) :
      getter(g)
    {}
    GetterVariant getter;
  };

  vector<Category> categories;

  template <typename G>
  void addGetter() {
    categories.emplace_back(new G()); // allocate here, transfer ownership to Sayer::categories
  }

};

编译器错误:

/usr/include/boost/variant/variant.hpp:1627:28: error: no matching member
      function for call to 'initialize'
              initializer::initialize(
              ~~~~~~~~~~~~~^~~~~~~~~~
/usr/include/boost/variant/variant.hpp:1798:9: note: in instantiation of function
      template specialization
      'boost::variant<std::unique_ptr<Getter<std::__cxx11::basic_string<char,
      std::char_traits<char>, std::allocator<char> > >,
      std::default_delete<Getter<std::__cxx11::basic_string<char,
      std::char_traits<char>, std::allocator<char> > > > >,
      std::unique_ptr<Getter<double>, std::default_delete<Getter<double> > >,
      std::unique_ptr<Getter<int>, std::default_delete<Getter<int> > >
      >::convert_construct<AgeGetter *>' requested here
        convert_construct( detail::variant::move(operand), 1L);

...

main.cpp:54:16: note: in instantiation of function template specialization
      'std::vector<Sayer::Category, std::allocator<Sayer::Category>
      >::emplace_back<AgeGetter *>' requested here
    categories.emplace_back(new G());
               ^
main.cpp:65:9: note: in instantiation of function template specialization
      'Sayer::addGetter<AgeGetter>' requested here
  sayer.addGetter<AgeGetter>();

...

/usr/include/boost/variant/detail/initializer.hpp:115:24: note: candidate
      function not viable: no known conversion from 'typename
      ::boost::move_detail::remove_reference<AgeGetter *&>::type'
      (aka 'AgeGetter *') to 'std::unique_ptr<Getter<int>,
      std::default_delete<Getter<int> > >' for 2nd argument
/usr/include/boost/variant/detail/initializer.hpp:149:17: note: candidate
      function not viable: requires 0 arguments, but 2 were provided
    static void initialize();

我该如何设置才能使内存所有权在容器中?

两件事:

首先,您必须在 Category 构造函数中移动 g,因为如果变体的任何成员是 non-copyable.[=24=,则变体是 non-copyable ]

其次,虽然 AgeGetter*Getter<int>*std::unique_ptr<Getter<int>>Category 链中的每个转换都是隐式的,但 C++ 只进行有限数量的隐式转换。所以基本上这条链太长了,你可以通过使用 emplace_back(std::make_unique<G>()) 而不是 emplace_back(new G()).

来修复它

此外,这更安全,因为如果 emplace_back 抛出(它可以抛出),new G() 不会被删除并因此泄漏。但如果 emplace_back 抛出,则 std::make_unique<G>() 返回的析构函数 unique_ptr 将被调用,因此不会有泄漏。您应该始终尽量避免在代码中使用原始 new