Search code examples
c++stdtupleparameter-pack

Construct a tuple with arbitrary number of types, all constructable from a single type, and non-default-construcible


I have a tuple

using MyTuple = std::tuple<Foo1, Foo2, Foo3, ... FooN>;

and want to construct MyTuple like this

MyTuple foo{Foo1{x}, Foo2{x}, Foo3{x}, ... FooN{x}};

  • FooK is not default constructible
  • N is not known at write-time. Thus I cannot hardcode a list like the one above.

What I have is

Enum::Tuple<EnumType, EnumTypeTraits> foo;

Which have been constructed from advice from here: Create template pack from set of traits, like so: https://github.com/milasudril/libenum/blob/master/libenum/tuple.hpp.

Do I need to add some ctor to Enum::Tuple to make it work?

Here is Enum::Tuple for reference:

namespace detail
{
    template<ContiguousEnum EnumType,
             template<EnumType>
             class EnumItemTraits,
             class T = std::make_integer_sequence<
                 std::underlying_type_t<EnumType>,
                 Size<EnumType>::value  // NOTE: calling distance here triggers ICE in gcc 10.2
                 >>
    struct make_tuple;

    template<ContiguousEnum EnumType,
             template<EnumType>
             class EnumItemTraits,
             std::underlying_type_t<EnumType>... indices>
    struct make_tuple<EnumType,
                      EnumItemTraits,
                      std::integer_sequence<std::underlying_type_t<EnumType>, indices...>>
    {
        using type =
            std::tuple<typename int_to_type<EnumType, EnumItemTraits, indices>::type...>;
    };
}

template<ContiguousEnum EnumType, template<EnumType> class EnumItemTraits>
class Tuple: private detail::make_tuple<EnumType, EnumItemTraits>::type
{
public:
    using Base       = typename detail::make_tuple<EnumType, EnumItemTraits>::type;
    using index_type = EnumType;
    template<index_type index>
    using traits = EnumItemTraits<index>;

    using Base::Base;

    static constexpr auto size() { return std::tuple_size_v<Base>; }

    template<index_type i>
    using tuple_element = std::tuple_element_t<distance(begin(Empty<EnumType>{}), i), Base>;

    template<index_type i>
    constexpr auto const& get() const
    {
        return std::get<distance(begin(Empty<EnumType>{}), i)>(base());
    }

    template<index_type i>
    constexpr auto& get()
    {
        return std::get<distance(begin(Empty<EnumType>{}), i)>(base());
    }

private:
    Base const& base() const { return *this; }

    Base& base() { return *this; }
};

Solution

  • As I understand, you want something like:

    template <typename... Ts> struct tag{};
    
    template <typename Tuple> struct tag_from_tuple;
    template <typename Tuple> using tag_from_tuple_t = typename tag_from_tuple<Tuple>::type;
    
    template <typename... Ts> struct tag_from_tuple<std::tuple<Ts...>>
    {
        using type = tag<Ts...>;
    };
    
    
    template <typename T, typename... Ts>
    std::tuple<Ts...> make_tuple_from_impl(const T& t, tag<Ts...>)
    {
        return {Ts{t}...};
    }
    
    template <typename Tuple, typename T>
    Tuple make_tuple_from(const T& t)
    {
        return make_tuple_from_impl(t, tag_from_tuple_t<Tuple>{});
    }
    

    Demo