Инициализировать статический std::map с unique_ptr в качестве значения

Как можно инициализировать статическую карту, где значение std::unique_ptr?

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

Конечно, это не работает (copy-ctor of std::unique_ptr удаляется).

Является ли это возможным?

3 ответа

Решение

Проблема в том, что построение из 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 ){
  ststic_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>> = 
  make_map(0, std::make_unique<MyClass>());

... за исключением опечаток.

Другой способ сделать это - использовать лямбду. это то же самое, что использование отдельной функции, но приближает инициализацию карты к действию. В этом случае я использовал комбинацию auto & и decltype, чтобы избежать необходимости называть тип карты, но это просто для удовольствия.

Обратите внимание, что аргумент, передаваемый в лямбду, является ссылкой на объект, который еще не был создан в точке вызова, поэтому мы не должны ссылаться на него каким-либо образом. Он используется только для вывода типа.

#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();
}

Вот другой способ. В этом случае мы передали временную лямбду и использовали копию 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();
}

И еще один способ, используя лямбда-захват в изменчивой лямбде.

#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();
}
Другие вопросы по тегам