-1

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

5
  • 1
    _reverse accepts a type_list with at least one element. There's no overload that can accept type_list<>. Commented Sep 6, 2025 at 12:53
  • 1
    Another specialization is needed because your function, you know, calls it. So _reverse<int,int,float> calls _reverse<int, float> which calls _reverse<float> which attempts to call _reverse<> - but _reverse requires at least one type, for First parameter. Hence the error. Commented Sep 6, 2025 at 12:59
  • 1
    You probably want an else so merge branch would be discarded when empty. Commented Sep 6, 2025 at 13:31
  • You could do it without the if and call to merge. I think something like this makes it clearer anyway: godbolt.org/z/35abMsTTn Commented Sep 6, 2025 at 13:49
  • I didn't vote to close, but I think the reason someone else did might be that this isn't a full MCVE. You may want to provide a simplified version of aml::type_list so people can plug the code into their environment of choice and compile without editing anything, presumably something like this: template<typename... Ts> struct type_list { constexpr bool empty() const { return sizeof...(Ts) == 0; } }; (You'll probably also want to add #include <iterator> and using std::empty;, so the empty(ls) compiles without issue.) Commented Sep 6, 2025 at 17:54

1 Answer 1

8

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.