Рекурсивный тип данных с частично фиксированными типами

У меня есть следующий код

#include <string_view>
#include <utility>

namespace std
{
  template <typename T1, typename T2>
  pair(T1 t1, T2 t2) -> pair<T1, T2>;
}

template<typename ... T>
struct node {};

template<typename head_t, typename ... tail_t>
struct node<head_t, tail_t ...>
{
  node(const head_t& head, const tail_t& ... tail)
    : head(head)
    , tail(tail...)
  {}

  head_t head;
  node<tail_t ... > tail;
};

template <typename... T>
node(T... t) -> node<T...>;

int main()
{
  node n{
    std::pair{std::string_view{"a"}, int{4}},
    std::pair{std::string_view{"b"}, int{5}},
    std::pair{std::string_view{"dqwd"}, node{
      std::pair{std::string_view{"asdas"}, float{3.4}}
    }
  };
  return 0;
}

который я компилирую с

g++ -Wall -Wextra -Wpedantic -std=gnu++17 -Wl,--wrap=malloc

Моя структура данных представляет собой рекурсивный список std::pair с первым элементом типа std::string_view. Теперь я хочу избавиться от std::pair и std::string_view при инициализации, потому что они всегда будут одного типа, как мне этого добиться? например:

node n{
  {"a", int{4}},
  {"b", int{5}},
  {"dqwd", node{
    {"asdas", float{3.4}}
  }
};

person Reza    schedule 08.05.2020    source источник
comment
Подождите, законно ли размещать пользовательские руководства по дедукциям в пространстве имен std?   -  person Konrad Rudolph    schedule 08.05.2020
comment
В этом случае руководство по дедукции для пары все равно не нужно.   -  person cigien    schedule 08.05.2020


Ответы (1)


Избавиться от string_view, по крайней мере, довольно легко. У него также есть бонус, заключающийся в устранении ваших манипуляций с пространством имен std, что, даже если бы оно было законным, все равно вызывало бы у меня дискомфорт.

Честно говоря, ваши манипуляции с std не являются такими ужасными в качестве примера здесь, потому что вы можете легко использовать свой собственный std::pair эквивалент и добиться того же синтаксиса.

#include <string_view>

template<typename T>
auto leaf(std::string_view s, T d) {
    return std::make_pair(s, std::move(d));
}

template<typename ... T>
struct node {};

template<typename head_t, typename ... tail_t>
struct node<head_t, tail_t ...>
{
  node(head_t head, tail_t... tail)
    : head(std::move(head))
    , tail(std::move(tail)...)
  {}

  head_t head;
  node<tail_t ... > tail;
};

template <typename... T>
node(T... t) -> node<T...>;

int main()
{
    node n{
        leaf("a", 4),
        leaf("b", 5),
        leaf("c", node{
            leaf("aaa", 12.4f)
        })
    };

  return 0;
}

Для избавления от листа может быть применимо следующее: https://stackoverflow.com/a/51857245/4442671, но подозреваю что нет.

В качестве примечания: ваш класс узла может просто делегировать std::tuple<>, что в основном делает то же самое. Это избавит вас от необходимости иметь дело с рекурсивной очисткой аргументов, и вам даже не понадобится руководство по дедукции:

template<typename... T>
struct node
{
  node(std::pair<std::string_view, T>... args)
    : childs_(std::move(args)...) {}

  std::tuple<std::pair<std::string_view, T>...> childs_;
};
person Frank    schedule 08.05.2020
comment
хорошая идея с std::tuple! - person Reza; 08.05.2020