Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template partial specialization: Why cant I match the last type in variadic-template?

I try to write a IsLast type traits to check if a given type is the last one in a std::tuple, but the code below does not compile. I know how to get around it but I am curious why the compiler does not like it.I guess there must be some rule on specialization of variadic-template that I am not aware of.

The code is at: https://godbolt.org/g/nXdodx

Error message:

error: implicit instantiation of undefined template
 'IsLast<std::tuple<std::__cxx11::basic_string<char>, int>, int>'

There is also a warning on specialization declaration:

warning: class template partial specialization contains template parameters that cannot be deduced; this partial specialization will never be used

#include <tuple>
#include <string>

/////////This works
template<typename TP, typename T>
struct IsFirst;

template<typename U, typename ...V, typename T>
struct IsFirst <std::tuple<U, V...>, T>
{
  enum {value = false};
};

template<typename U, typename ...V>
struct IsFirst <std::tuple<U, V...>, U>
{
  enum {value = true};
};

////////This doesn't compile
template<typename TP, typename T>
struct IsLast;

template<typename ...U, typename V, typename T>
struct IsLast <std::tuple<U..., V>, T>
{
  enum {value = false};
};

template<typename ...U, typename V>
struct IsLast <std::tuple<U..., V>, V>
{
  enum {value = true};
};


int main()
{
  using T = std::tuple<std::string, int>;
  bool v1 = IsFirst<T, std::string>::value;
  bool v2 = IsLast <T, int>::value;
}
like image 510
xin wang Avatar asked Feb 04 '17 16:02

xin wang


2 Answers

In a class template, the parameter pack must come after all other template parameters, so something like this is not allowed.

template<typename U, typename ...V, typename T>
struct IsFirst <std::tuple<U, V...>, T>
{
  enum {value = false};
};

In a function template, there can be other template parameters after the pack only if they can be deduced. This is not allowed for class templates because they do not allow deduction.

like image 80
Sam Marinelli Avatar answered Oct 23 '22 10:10

Sam Marinelli


As observed by Resurrection, variadic template must be in last position.

But there are a lot of other ways to obtain the same result.

The obvious solution is to create a recursive type traits, but I show you a solution based on std::tuple_element

#include <tuple>
#include <iostream>
#include <type_traits>

template <typename C, typename T>
struct IsLast;

template <template <typename ...> class C, typename T, typename ... Ts>
struct IsLast<C<Ts...>, T>
 {
   using Tl
    = typename std::tuple_element<sizeof...(Ts)-1U, std::tuple<Ts...>>::type;

   static constexpr bool value { std::is_same<Tl, T>::value };
 };

int main ()
 {
   using T = std::tuple<std::string, int>;

   std::cout << IsLast<T, int>::value << std::endl;  // print 1
   std::cout << IsLast<T, long>::value << std::endl; // print 0
 }

Observe that this IsLast works with

 T = std::pairs<int, std::string>

and with other template classes.

like image 31
max66 Avatar answered Oct 23 '22 09:10

max66