Home > Net >  Variadic template errors in struct template but not in function template
Variadic template errors in struct template but not in function template

Time:10-12

Why this code does not work?

template <size_t counter, typename TTag, typename Head, typename... Tails>
struct Tag2IDImpl {
  constexpr static int value = std::is_same_v<TTag, Head>? counter : Tag2IDImpl<counter 1, TTag, Tails...>::value;
};

template <typename TTag, typename... TParameters>
struct Tag2ID {
  constexpr static int value = Tag2IDImpl<0, TTag, TParameters...>::value;
};

struct A;
struct B;
struct C;
struct D;


int main() {
  std::cout<<Tag2ID<A, B, C, D,A>::value<<std::endl;
}

I got error like this:

vartypedict.cpp: In instantiation of ‘constexpr const int Tag2IDImpl<3, A, A>::value’:
vartypedict.cpp:39:109:   recursively required from ‘constexpr const int Tag2IDImpl<1, A, C, D, A>::value’
vartypedict.cpp:39:109:   required from ‘constexpr const int Tag2IDImpl<0, A, B, C, D, A>::value’
vartypedict.cpp:44:69:   required from ‘constexpr const int Tag2ID<A, B, C, D, A>::value’
vartypedict.cpp:97:36:   required from here
vartypedict.cpp:39:109: error: wrong number of template arguments (2, should be at least 3)
   39 |   constexpr static int value = std::is_same_v<TTag, Head>? counter : Tag2IDImpl<counter 1, TTag, Tails...>::value;
      |                                                                                                             ^~~~~
vartypedict.cpp:38:8: note: provided fortemplate<long unsigned int counter, class TTag, class Head, class ... Tails> struct Tag2IDImpl38 | struct Tag2IDImpl {

If I use function template as follows, it works:

template <size_t counter, typename TTag, typename THead, typename...TTails>
constexpr size_t Tag2IDImpl() {
  if(std::is_same_v<TTag, THead>) {
    return counter;
  } else {
    if constexpr(sizeof...(TTails)>0) {
      return Tag2IDImpl<counter 1, TTag, TTails...>();
    } else {
      return counter 1;
    }
  }

}

template <typename TTag, typename... TParameters>
constexpr size_t Tag2ID() {
  return Tag2IDImpl<0, TTag, TParameters...>();
}

My question is why functional template recursive call "Tag2IDImpl<counter 1, TTag, TTails...>()" can match to the function template "template <size_t counter, typename TTag, typename THead, typename...TTails>", which using struct, it complains mismatch?

CodePudding user response:

My guess is that you didn't have a good recursive end evaluation. I'm more used to using constexpr template functions, using constexpr if, by now then using structs to do the recursive calculations. So here's my take on it: (Note I started counting at 1, so I get 4 as id, for 4 different types, if that's what you where trying to do)

#include <type_traits>
#include <iostream>

template <std::size_t counter, typename TTag, typename Head, typename... Tails>
constexpr static int Tag2IDImpl()
{
    if constexpr (sizeof...(Tails) > 0)
    {
        return  std::is_same_v<TTag, Head> ? counter : Tag2IDImpl<counter   1, TTag, Tails...>();
    }
    else
    {
        // the recursion on the last step should be special and your code doesn't do that.
        return std::is_same_v<TTag, Head> ? counter : counter   1;
    }
}


template <typename TTag, typename... TParameters>
constexpr static int Tag2ID()
{
    return Tag2IDImpl<1, TTag, TParameters...>();
}

struct A;
struct B;
struct C;
struct D;


int main()
{
    // outputs 4 distinct types
    std::cout << Tag2ID<A, B, C, D, A>() << std::endl;
}

CodePudding user response:

When you use if constexpr, the if the constexpr bool is valid, then else branch isn't evaluated at compile time, whereas when you used the operator ? you need both the branches to be valid. For avoiding the if, try partially specializing the exit condition, something like:

template <size_t N, typename TTag, typename Head, typename... Tails>
struct Tag2IDImpl {
    static constexpr size_t value = Tag2IDImpl<N 1,TTag,Tails...>::value;
};

template <size_t N, typename TTag>
struct Tag2IDImpl<N,TTag,TTag> {
    static constexpr size_t value = N;
};

template <typename TTag, typename... TParameters>
constexpr size_t Tag2ID() {
  return Tag2IDImpl<0,TTag, TParameters...>::value;
}
  • Related