2

in following code:

#include <iostream>
#include <tuple>

template<typename T>
struct Container
{
  std::string id;
  T value;

  Container(std::string id, T value) : id(id), value(value) {}
};

template<typename... T>
struct ElementCodec
{
  std::tuple<T...> values;

  ElementCodec(T... args) : values(args...) {}
};

template<typename... T> ElementCodec(T...) -> ElementCodec<T...>;

int main()
{
  ElementCodec a { int { 5 }, double { 3. }, Container { "52", 7 } };
  auto [x, y, container] = a.values;

  std::cout << x << ", " << y << ", " << container.value << '\n';

}

After the specialization of the given code the tuple values is of type std::tuple<int, double, Container<int>>. What I'd like to do is decaying it to the type stored in the container, so std::tuple<int, double, int> to make th access via container.value not necessary.

Is this possible to do in c++17? I've been stuck on this problem for a while now and can't really find any ressources about this.

Barry
  • 286,269
  • 29
  • 621
  • 977
user3520616
  • 60
  • 3
  • 17

1 Answers1

7

You could unwrap the Container<T>s with a helper function:

template <typename T> T unwrap(T value)        { return value; }
template <typename T> T unwrap(Container<T> c) { return c.value; }

template <typename T> using unwrap_t = decltype(unwrap(std::declval<T>()));

And then adjust your template and deduction guide accordingly:

template <typename... T>
struct ElementCodec
{
  std::tuple<T...> values;

  template <typename... Us>
  ElementCodec(Us... args) : values(unwrap(args)...) {}
};

template<typename... T>
ElementCodec(T...) -> ElementCodec<unwrap_t<T>...>;

The above doesn't work on gcc (see this answer) , you can fix it with a constraint:

template <typename... Us,
  std::enable_if_t<std::is_constructible_v<
    std::tuple<T...>, unwrap_t<Us>...
    >, int> = 0>
ElementCodec(Us... args) : values(unwrap(args)...) {}
Barry
  • 286,269
  • 29
  • 621
  • 977