Инициализировать структуру C ++ как массив неизвестных границ во время компиляции - PullRequest
0 голосов
/ 15 февраля 2019

Я могу написать это, и это прекрасно работает:

struct Foo
{
  int i;
  std::string s;
};

const Foo foo[] = {
  { 42, "the answer to the ultimate questions" },
  { 23 /*initializing only the first member, 's' gets the default value*/ }
};

Что я хочу сделать, это иметь структуру, обертывающую массив, чтобы я мог добавить к ней методы:

template<typename V1, typename V2, size_t Count>
struct Map
{
  std::array<std::pair<V1, V2>, Count> mappings;
  //or
  //std::pair<V1, V2> mappings[Count];

  V1 operator()(const V2&) const;
  V2 operator()(const V1&) const;
};

И я хочу инициализировать его как массив неизвестных границ следующим образом:

constexpr Map<int, std::string_view, /*???*/> = {
  { 42, "the answer to the ultimate question" },
  { 23, "some other stuff" },
  { /*...*/ }
};

Но тогда возникает проблема, что вам необходимо указать параметр шаблона Count, который я не хочу делатьЯ хочу, чтобы он работал как в случае с массивом.

Я думал, что функция, возвращающая такой объект, справится с задачей, как это:

template<typename V1, typename V2, typename... Args>
constexpr auto makeMap(Args... args)
{
  return Map<V1, V2, sizeof...(Args)>{ args... };
}

, которая затем позволяет использовать его какthis:

using Item = std::pair<int, std::string_view>;

constexpr auto map = makeMap<int, std::string_view>(
  Item{ 42, "the answer to the ultimate questions" },
  Item{ 23, "some other stuff" }
);

Но если вы опустите тип Item, то создание экземпляра шаблона не сможет определить типы аргументов, что запрещает использование, которое я изначально хотел:

constexpr auto map = makeMap<int, std::string_view>(
  { 42, "the answer to the ultimate questions" },
  { 23, "some other stuff" }
);

В настоящее время яСчитаю, что это невозможно, но все равно хотел спросить, если я что-то упустил.

Исследуя это, я нашел предложение , которое дает именно то, что я хочу.

В любом случае, я хотел бы получить любые идеи.

Ответы [ 2 ]

0 голосов
/ 15 февраля 2019

Рекурсивно предложив Jarod42, я предлагаю рекурсивную структуру MakeMyMap с static func(), которая получает последовательность аргументов std::pair<T1, T2> [наблюдение: 42 - верхняя граница по умолчаниюдля количества std::pair аргументов].

template <typename T1, typename T2, std::size_t Dim>
struct MyMap
 {
   std::array<std::pair<T1, T2>, Dim> map;
 };

template <typename T, std::size_t>
using getTheType = T;

template <typename, typename, typename = std::make_index_sequence<42u>>
struct MakeMyMap;

template <typename T1, typename T2, std::size_t ... Is>
struct MakeMyMap<T1, T2, std::index_sequence<Is...>>
   : public MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>
 {
   using MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>::func;

   static auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
    { return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
 };

template <typename T1, typename T2>
struct MakeMyMap<T1, T2, std::index_sequence<>>
 {
   static auto func ()
    { return MyMap<T1, T2, 0u>{ }; }
 };

Таким образом, вы можете написать

   auto map = MakeMyMap<int, std::string>::func(
      { 42, "the answer to the ultimate questions" },
      { 23, "some other stuff" }
      );

Ниже приведена полная компиляция (достаточно C ++ 14), пример

#include <array>
#include <string>
#include <utility>

template <typename T1, typename T2, std::size_t Dim>
struct MyMap
 {
   std::array<std::pair<T1, T2>, Dim> map;
 };

template <typename T, std::size_t>
using getTheType = T;

template <typename, typename, typename = std::make_index_sequence<42u>>
struct MakeMyMap;

template <typename T1, typename T2, std::size_t ... Is>
struct MakeMyMap<T1, T2, std::index_sequence<Is...>>
   : public MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>
 {
   using MakeMyMap<T1, T2, std::make_index_sequence<sizeof...(Is)-1u>>::func;

   static auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
    { return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
 };

template <typename T1, typename T2>
struct MakeMyMap<T1, T2, std::index_sequence<>>
 {
   static auto func ()
    { return MyMap<T1, T2, 0u>{ }; }
 };

int main ()
 {
   auto map = MakeMyMap<int, std::string>::func(
      { 42, "the answer to the ultimate questions" },
      { 23, "some other stuff" }
      );

   static_assert( std::is_same<decltype(map),
                               MyMap<int, std::string, 2u>>::value, "!" );
 }

Используя C ++ 17, вы можете использовать std::string_view вместо std::string, вы можете определить constexpr функции func(), поэтому map может быть constexpr

   constexpr auto map = MakeMyMap<int, std::string_view>::func(
      { 42, "the answer to the ultimate questions" },
      { 23, "some other stuff" }
      );

и вы также можете проверить, что

   static_assert( std::is_same<decltype(map),
                               MyMap<int, std::string_view, 2u> const>::value, "!" );

Используя новый C ++ 17 variadic using, вы можете избежать рекурсии при всех переключениях MakeMyMap следующим образом

template <typename, typename, typename = std::make_index_sequence<42u>>
struct MakeMyMap;

template <typename T1, typename T2, std::size_t ... Is>
struct MakeMyMap<T1, T2, std::index_sequence<Is...>>
   : public MMM_helper<T1, T2, std::make_index_sequence<Is>>...
 { using MMM_helper<T1, T2, std::make_index_sequence<Is>>::func...; };

где MMM_helper (помощник Make My Map) определяется следующим образом

template <typename, typename, typename>
struct MMM_helper;

template <typename T1, typename T2, std::size_t ... Is>
struct MMM_helper<T1, T2, std::index_sequence<Is...>>
 {
   static constexpr auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
    { return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
 };

Ниже приводится полный C ++ 17, не рекурсивный, пример

#include <array>
#include <string_view>
#include <utility>

template <typename T1, typename T2, std::size_t Dim>
struct MyMap
 {
   std::array<std::pair<T1, T2>, Dim> map;
 };

template <typename T, std::size_t>
using getTheType = T;

template <typename, typename, typename>
struct MMM_helper;

template <typename T1, typename T2, std::size_t ... Is>
struct MMM_helper<T1, T2, std::index_sequence<Is...>>
 {
   static constexpr auto func (getTheType<std::pair<T1, T2>, Is> const & ... ps)
    { return MyMap<T1, T2, sizeof...(Is)>{ { { ps... } } }; }
 };

template <typename, typename, typename = std::make_index_sequence<42u>>
struct MakeMyMap;

template <typename T1, typename T2, std::size_t ... Is>
struct MakeMyMap<T1, T2, std::index_sequence<Is...>>
   : public MMM_helper<T1, T2, std::make_index_sequence<Is>>...
 { using MMM_helper<T1, T2, std::make_index_sequence<Is>>::func...; };

int main ()
 {
   constexpr auto map = MakeMyMap<int, std::string_view>::func(
      { 42, "the answer to the ultimate questions" },
      { 23, "some other stuff" }
      );

   static_assert( std::is_same<decltype(map),
                               MyMap<int, std::string_view, 2u> const>::value, "!" );
 }
0 голосов
/ 15 февраля 2019

Используя предложенный to_array:

template<typename V1, typename V2, size_t N>
constexpr auto makeMap(std::pair<V1, V2> const (&a)[N])
{
  return Map<V1, V2, N>{ to_array<std::pair<V1, V2>>(a) };
}

constexpr auto map = makeMap<int, std::string_view>({
  { 42, "the answer to the ultimate question" },
  { 23, "some other stuff" },
  { /*...*/ }
});

Если ваш компилятор поддерживает основы библиотеки TS v2, вы можете найти реализацию to_array в заголовке <experimental/array> внутрипространство имен std::experimental.

...