Reza
Reza

Reputation: 360

Recursive data type with partly fixed types

I have the following code

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

which I compile with

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

My data structure is a recursive list of a std::pair with the first element of type std::string_view. Now I want to get rid of std::pair and std::string_view in the initalization because they will always be the same type, how can I achieve this? e.g:

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

Upvotes: 2

Views: 99

Answers (1)

user4442671
user4442671

Reputation:

Getting rid of the string_view, at the very least, is pretty easy. It also has the bonus of eliminating your manipulation of the std namespace, which, even if it was legal, would still be making me very uncomfortable.

To be fair, your manipulation of std is not that horrible as an example here, because you could easily use your own std::pair equivalent and reach the same syntax.

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

For getting rid of leaf, The following might be applicable: https://stackoverflow.com/a/51857245/4442671, but I suspect not.

As a side note, your node class could simply delegate to std::tuple<> which does mostly the exact same thing. This would prevent you from having to deal with recursive peeling of the arguments, and you don't even need a deduction guide:

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_;
};

Upvotes: 1

Related Questions