初始化静态 std::map unique_ptr 作为值

Initialize static std::map with unique_ptr as value

本文关键字:ptr unique 静态 std map 初始化      更新时间:2023-10-16

如何初始化静态映射,其中值std::unique_ptr

static void f()
{
    static std::map<int, std::unique_ptr<MyClass>> = {
        { 0, std::make_unique<MyClass>() }
    };
}

当然这是行不通的(删除了std::unique_ptr的复制者)。

可能吗?

问题是从std::initializer-list构造会复制其内容。(std::initializer_list中的对象本质上是const的)。要解决您的问题:您可以从单独的函数初始化地图...

std::map<int, std::unique_ptr<MyClass>> init(){
    std::map<int, std::unique_ptr<MyClass>> mp;
    mp[0] = std::make_unique<MyClass>();
    mp[1] = std::make_unique<MyClass>();
    //...etc
    return mp;
}

然后称之为

static void f()
{
    static std::map<int, std::unique_ptr<MyClass>> mp = init();
}

在科里鲁现场观看

编写定制的新月代码似乎很无聊,并且妨碍了清晰度。

下面是相当有效的通用容器初始化代码。 它像初始值设定项列表一样将您的数据存储在临时std::array中,但它会移出而不是使其const

make_map采用偶数个元素,第一个是键,第二个值。

template<class E, std::size_t N>
struct make_container_t{
  std::array<E,N> elements;
  template<class Container>
  operator Container()&&{
    return {
      std::make_move_iterator(begin(elements)),
      std::make_move_iterator(end(elements))
    };
  }
};
template<class E0, class...Es>
make_container_t<E0, 1+sizeof...(Es)>
make_container( E0 e0, Es... es ){
  return {{{std::move(e0), std::move(es)...}}};
}
namespace details{
  template<std::size_t...Is, class K0, class V0, class...Ts>
  make_container_t<std::pair<K0,V0>,sizeof...(Is)>
  make_map( std::index_sequence<Is...>, std::tuple<K0&,V0&,Ts&...> ts ){
    return {{{
      std::make_pair(
        std::move(std::get<Is*2>(ts)),
        std::move(std::get<Is*2+1>(ts))
      )...
    }}};
  }
}
template<class...Es>
auto make_map( Es... es ){
  static_assert( !(sizeof...(es)&1), "key missing a value?  Try even arguments.");
  return details::make_map(
    std::make_index_sequence<sizeof...(Es)/2>{},
    std::tie( es... )
  );
}

这应该将其减少到:

static std::map<int, std::unique_ptr<MyClass>> bob = 
  make_map(0, std::make_unique<MyClass>());

。除非有错别字。

活生生的例子。

另一种方法是使用 lambda。 这与使用单独的函数相同,但使地图的初始化更接近操作。在这种情况下,我使用了auto&和decltype的组合来避免命名地图的类型,但这只是为了好玩。

请注意,传递给 lambda 的参数是对在调用时尚未构造的对象的引用,因此我们不得以任何方式引用它。它仅用于类型推断。

#include <memory>
#include <map>
#include <utility>
struct MyClass {};

static auto& f()
{
  static std::map<int, std::unique_ptr<MyClass>> mp = [](auto& model)
  {
    auto mp = std::decay_t<decltype(model)> {};
    mp.emplace(0, std::make_unique<MyClass>());
    mp.emplace(1, std::make_unique<MyClass>());
    return mp;
  }(mp);
  return mp;
}
int main()
{
  auto& m = f();
}

这是另一种方式。在本例中,我们已将临时变量传递到 lambda 中,并依赖于 copy elision/RVO。

#include <memory>
#include <map>
#include <utility>
struct MyClass {};
static auto& f()
{
  static auto mp = [](auto mp)
  {
    mp.emplace(0, std::make_unique<MyClass>());
    mp.emplace(1, std::make_unique<MyClass>());
    return mp;
  }(std::map<int, std::unique_ptr<MyClass>>{});
  return mp;
}
int main()
{
  auto& m = f();
}

还有另一种方式,在可变的 lambda 中使用 lambda 捕获。

#include <memory>
#include <map>
#include <utility>
struct MyClass {};
static auto& f()
{
  static auto mp = [mp = std::map<int, std::unique_ptr<MyClass>>{}]() mutable
  {
    mp.emplace(0, std::make_unique<MyClass>());
    mp.emplace(1, std::make_unique<MyClass>());
    return std::move(mp);
  }();
  return mp;
}
int main()
{
  auto& m = f();
}