шаблон шаблона пакет переменных параметров
Может кто-нибудь объяснить приведенный ниже код с пакетами параметров шаблона. Как это работает? Как в этом случае упаковываются и распаковываются параметры шаблона:
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 {};
Возможное запрошенное использование (на основе перегрузки шаблона функции или специализации для внутреннего типа шаблона std:: vector
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);
}
1 ответ
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 asvoid
or as non-existing (SFINAE), this is a common usewe 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 therequires
syntax
Other relevant SO questions:
1If 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.