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.