Home > Enterprise >  c metaprogramming using vs typedef in template
c metaprogramming using vs typedef in template

Time:01-13

I wrote the following toy code, but I can't figure out why with using alias I get a compilation error, but using typedef the code compiles successfully? I tried gcc and clang but both compilers give error.

: In substitution of 'template using nth_type_t = typename nth_type::type [with long unsigned int I = 1; T = type_list]': :47:36: required from here :41:7: error: no type named 'type' in 'struct nth_type >' 41 | using nth_type_t = typename nth_type::type; | ^~~~~~~~~~ : In substitution of 'template using nth_type_t = typename nth_type::type [with long unsigned int I = 2; T = type_list]': :48:36: required from here :41:7: error: no type named 'type' in 'struct nth_type >' Compiler returned: 1
#include <type_traits>
#include <utility>

template<typename... Ts> struct composition : Ts... { using Ts::operator()...; };
template<typename... Ts> composition(Ts...) -> composition<Ts...>;

template<typename T, template<typename...> typename Tmpl>
concept is_template = decltype(composition {
    []<typename ...U>(const Tmpl<U...> &) { return std::true_type{}; },
    [](const auto &) { return std::false_type{}; } }(std::declval<T>()))::value;

template<typename... Ts>
struct type_list {};

template <std::size_t I, typename T>
struct indexed { using type = T; };

template <typename Is, typename ...Ts>
struct indexer;

template <std::size_t ...Is, typename ...Ts>
struct indexer<std::index_sequence<Is...>, Ts...> : indexed<Is, Ts>... {};

template<typename ...Ts>
auto make_indexer() { return indexer<std::index_sequence_for<Ts...>, Ts...>{}; }

template <std::size_t I, typename ...Ts>
using nth_element_t = typename decltype([]<typename T>(const indexed<I, T>&){return std::type_identity<T>{};}(make_indexer<Ts...>()))::type;

template<std::size_t I, is_template<type_list> T>
struct nth_type;

template<std::size_t I, typename ... Ts>
struct nth_type< I, type_list<Ts...> >
{
    typedef nth_element_t<I, Ts...> type;
//    using type = nth_element_t<I, Ts...>; // Compile error!
};

template<std::size_t I, typename T>
using nth_type_t = typename nth_type<I, T>::type;

int main()
{
    using list = type_list<int, float, double>;

    using elm1 = nth_type_t<1, list>;
    using elm2 = nth_type_t<2, list>;

    return 0;
}

CodePudding user response:

It seems as if GCC has a problem with the C 20 feature of a template lambda; you can get around (and simplify your solution a bit) by falling back to re-building the template lambda with pre-C 20 means as follows:

///////////////////////////////////////////////////
// the indexer you already have:

template <std::size_t I, typename T>
struct indexed { using type = T; };

template <typename Is, typename ...Ts>
struct indexer;

template <std::size_t ...Is, typename ...Ts>
struct indexer<std::index_sequence<Is...>, Ts...> : indexed<Is, Ts>... {};

///////////////////////////////////////////////////
// the template lambda replacement:

template <size_t I>
struct nth_element
{
    template <typename T>
    auto operator()(indexed<I, T>) -> T; // don't need an implementation for
};

///////////////////////////////////////////////////
// using the struct:

template <std::size_t I, typename ...Ts>
using nth_element_t = decltype
(
   std::declval<nth_element<I>>()(indexer<std::index_sequence_for<Ts...>, Ts...>())
);

So far this would select the n-th element from any variadic template list. You can map this now to the type list template; note that you actually can spare the entire is_template effort, as the template only is implemented for the typelist template anyway:

template<typename... Ts>
struct type_list { };

// simplified base template
template<std::size_t I, typename T>
struct nth_type;

// specialisation for the type list:
template<std::size_t I, typename ... Ts>
struct nth_type<I, type_list<Ts...>>
{
    // now works with GCC, too:
    using type = nth_element_t<I, Ts...>;
};

template<std::size_t I, typename T>
using nth_type_t = typename nth_type<I, T>::type;

This now even works with C 14 (for the index sequence; if you re-build that on your own this should even be fine for C 11); demonstration on godbolt.

  • Related