1

Can someone please explain the below code with template parameter packs. How does it work? How are the template parameters packed and unpacked in this case:

template<typename Test, template<typename...> class Ref> //#6
struct is_specialization : std::false_type {};

template<template<typename...> class Ref, typename... Args> //#7
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};

Possible requested usage (inspired by Function Template Overloading or Specialization for inner template type std::vector<std::vector<T>>)

template <typename T>
bool f(T& x) // #1
{
    std::cout << "body of f\n";
    return f(x);
}

template <typename T>
bool f(std::vector<T>& v) // #2
{
    std::cout << "body of f for vectors\n";
    return true;
}

template<typename T>
typename std::enable_if<
    is_specialization<typename T::value, std::vector>::value, T>::type
bool f(std::vector<T>& v) // #5
{
    std::cout << "body of f for vectors<vectors>\n";
    return true;
}

int main() {
  std::vector<int> v{1,2}
  f(v); 
}
Amir Kirsh
  • 12,564
  • 41
  • 74
TechTotie
  • 127
  • 1
  • 15
  • 1
    I am confused. #2 should be bound unconditionally since that is the best match here. #5 doesn't even come into play here, excluding any and all SFINAE. What exactly is not clear to you? Leaving aside the issue that #5 shouldn't even compile unless you mean `template::value, T>::type>`. – Tanveer Badar Jan 13 '20 at 07:24
  • @TanveerBadar: Can you please refer the complete code in the link given. I have made edits to the question. Please have a look. – TechTotie Jan 13 '20 at 09:04
  • @TechTotie What exactly do you need clarification on? Which part of the `is_specialization` definition do you not understand? – walnut Jan 13 '20 at 09:26
  • @walnut Please let me know how #5 works and how is_specialization works w.r.t template parameter packing and unpacking. – TechTotie Jan 13 '20 at 10:28

1 Answers1

3

Below are some explanations on variadic templates syntax, packing and unpacking -- on the specific code in question and how to make it work 1.


It seems that what you want to achieve is to differentiate between std::vector<int> and std::vector<float>.

However

Your function #1 is too greedy and would take all possible arguments:

template <typename T>
bool f(T& x) // #1
{
    std::cout << "body of f\n";
    return f(x);
}

which would result with ambiguity if any call fits also one of the overloaded versions.

So, we first need to:

Separate between is_vector or not

We can achieve that with the following code:

// [A]
template <class, template <class...> class>
struct is_of_template_type : std::false_type {};

// [B]
template <class T, class... Args, template <class...> class U>
struct is_of_template_type<U<T, Args...>, U> : std::true_type {};

// [C]
template <class Something>
struct is_vector: is_of_template_type<Something, std::vector> {};

[A] is the base template (nothing to do with inheritance) for the generic case, before any specialization, for allowing to test if a given type is a specific template. This template arguments are: (a) some type (b) some other type that must be a template, with some unknown template arguments.

[B] is the specialization for the true case. The caller should provide two template parameters but it would fit this specialization only if the first template parameter fits the template type given as the second template parameter. Note that the expression expects two template parameters: (a) a template argument U<T, Args...> from which we will infer the types T and Args, and (b) another template argument -- which must be a template argument because of the base template -- for which we ignore the inner template arguments, as we just need the first type to match the second, regardless of the inner template arguments.

[C] is the specific usage for checking if a given type is a vector, without the need to deal with the vector template parameters.


Now we can rewrite function #1 to:

template<typename Something>
typename std::enable_if<!is_vector<Something>::value>::type
f(const Something& v) // #1
{
    std::cout << "body of f for generic Something\n";
}

and it is not so greedy as before, as it takes only non-vectors.


Now we are ready for our next task:

Separate between different kind of vectors, i.e. is_vector_of_T

To achieve that we would add the following:

template <typename Container, typename T>
struct is_vector_of_T: std::false_type {};

template <typename T>
struct is_vector_of_T<std::vector<T>, T>: std::true_type {};

and now we can have separate functions for std::vector<int> and std::vector<float>:

template<typename Something>
typename std::enable_if<is_vector_of_T<Something, int>::value>::type
f(const Something& v) // #2
{
    std::cout << "body of f for vector<int>\n";
}

template<typename Something>
typename std::enable_if<is_vector_of_T<Something, float>::value>::type
f(const Something& v) // #3
{
    std::cout << "body of f for vector<float>\n";
}

Can we use it to isolate std::vector<std::vector<int>>?

Yes we can:

template<typename Something>
typename std::enable_if<is_vector_of_T<Something, std::vector<int>>::value>::type
f(const Something& v) // #4
{
    std::cout << "body of f for vector<vector<int>>\n";
}

template<typename Something>
typename std::enable_if<is_vector_of_T<Something, std::vector<float>>::value>::type
f(const Something& v) // #5
{
    std::cout << "body of f for vector<vector<float>>\n";
}

Code: https://godbolt.org/z/EFeGZk


Notes:

  • I use enable_if in all cases above to declare the return value of the method as void or as non-existing (SFINAE), this is a common use

  • we may consider instead of overloading template functions to specialize template classes, it may reduce the need of enable_if

  • with C++20 we would replace the use of enable_if with the requires syntax


Other relevant SO questions:


1 If variadic templates packing and unpacking is entirely new to you I would suggest starting to learn this topic from some more basic examples like this or this. The question is specifically related to template template parameter (the duplicate template is not a mistake) which is a more advanced topic, you can follow this as a good reference. Then, the question is more specifically related to variadic template template parameter, related to examples like this and this.

Amir Kirsh
  • 12,564
  • 41
  • 74