问题
I recently asked a question about determining whether an iterator points to a complex value at compile time and received an answer that works.
The question is here: How can I specialize an algorithm for iterators that point to complex values?
And the solution was a set of templates that determine whether one template is a specialization of another:
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
This does work, but I am really struggling to understand how this works. Particularly the nested template within a template is confusing to me. I'm also still fairly new to using variadic templates and it seems odd to have a variadic template with no type provided, for example: <class...> instead of something like this <class... Args>.
Can someone please break down this template and describe how it gets resolved?
回答1:
You have to take in count that there are three types of template parameters:
1) types
2) non-types (or values)
3) template-templates
The first type is preceded by typename (or class)
template <typename T>
void foo (T const & t);
In the preceding example, T is a type and t (a classical function argument) is a value of type T.
The second type of template parameter are values and are preceded by the type of the value (or auto, starting from C++17, for a not specified type)
template <int I>
void bar ()
{ std::cout << I << std::endl; }
In the preceding example the I template parameter is a value of type int.
The third type is the most complex to explain.
Do you know (I suppose) that std::vector<int> and std::vector<double> are different types, but they have in common std::vector, a template class.
A template-template parameter is a parameter that accept std::vector, the template class without arguments.
A template-template parameter is preceded by a template keyword, as in the following example
template <template <int> class C>
void baz ();
The template-template parameter C in the preceding example is class (or struct) that require a single int (value) template parameter.
So if you have a class
template <int I>
class getInt
{ };
you can pass getInt, as template parameter, to baz()
baz<getInt>();
Now you should be able to understand your code:
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
the is_specialization struct is a template struct that receive, as template parameters, a type (T) and a template-template Template that accept classes/structs receiving a variadic number of type template parameters.
Now you have a specialization of is_specialization:
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
This specialization is selected when the first template parameter (Template<Args...>) is a class based on the second (Template).
An example: if you instantiate
is_specialization<std::vector<int>, std::map>
the main version (that inherit from std::false_type) is selected because std::vector<int> isn't based on std::map.
But if you instantiate
is_specialization<std::vector<int>, std::vector>
the specialization (that inherit from std::true_type) is selected because std::vector<int> is based on std::vector.
来源:https://stackoverflow.com/questions/59976065/how-do-nested-templates-get-resolved-in-c