In c++ when i try to write a code that returns reverse of a type list what i wrote( a user defined template class):
template <typename ...V,typename ...D>
constexpr decltype(auto) merge(const type_list<V...>& first, const type_list<D...>& second)
{
return type_list<V...,D...>();
}
template<typename First, typename ...Var>
constexpr decltype(auto) _reverse(type_list<First,Var...> ls)
{
if constexpr (empty(ls)) return type_list<>();
return merge(_reverse(type_list<Var...>()),type_list<First>());
}
and when i try to run:
aml::type_list<int,float> ls;
auto x = aml::_reverse(ls);
clang says:
In file included from test.cpp:4:
././lesson_4.hpp:64:16: error: no matching function for call to '_reverse'
64 | return merge(_reverse(type_list<Var...>()),type_list<First>());
| ^~~~~~~~
././lesson_4.hpp:64:16: note: in instantiation of function template specialization 'aml::_reverse<float>' requested here
test.cpp:10:16: note: in instantiation of function template specialization 'aml::_reverse<int, float>' requested here
10 | auto x = aml::_reverse(ls);
| ^
././lesson_4.hpp:61:27: note: candidate template ignored: failed template argument deduction
61 | constexpr decltype(auto) _reverse(type_list<First,Var...> ls)
| ^
1 error generated.
Could you please explain me why compiler just can't instantiate for example reverse<int,int,float> function template <typename First = int, template ...Other = {int,float}>? And why it says another specialation is needed like <int,float>? because i think they are also an instance of typename First, typename ...Other specialation
The problem is that your function doesn't actually cover all cases; it misses the case where type_list
's template parameter pack is empty. And that's a pretty important case, since it's the one _reverse()
falls back on to end its recursion!
Ultimately, there are two ways you can fix it. You can either provide a separate overload to catch the empty parameter pack, or you can modify _reverse()
to check if Var...
is empty instead. The first would look like this:
// Empty list overload.
constexpr decltype(auto) _reverse(type_list<> ls) {
return type_list<>();
}
// Your _reverse().
template<typename First, typename ...Var>
constexpr decltype(auto) _reverse(type_list<First,Var...> ls)
{
if constexpr (empty(ls)) return type_list<>();
return merge(_reverse(type_list<Var...>()),type_list<First>());
}
And the second would look like this:
// Modified _reverse().
// Note the compound if constexpr. Branches must be mutually exclusive, the first branch WILL
// fail to compile if the merge() call is visible. if constexpr shields `type_list<Var...>` from
// empty `Var...`, to prevent invalid `_reverse(type_list<>())` call.
template<typename First, typename... Var>
constexpr decltype(auto) _reverse(type_list<First, Var...> ls) {
// Either check will work here; one requires type_list::size(), one doesn't.
//if constexpr (size(ls) == 1) { // Requires type_list::size().
if constexpr (sizeof...(Var) == 0) { // Uses Var... pack's size directly.
return type_list<First>();
} else {
return merge(_reverse(type_list<Var...>()), type_list<First>());
}
}
You can see both versions here, the #if
lets you choose between them.