This answer forward declare template class Memo one way, and implemented it's partial specialization in another way.
So, the forward declaration is :
template <template <typename...> class Container, typename...> struct Memo;
and the partial specialization this :
template <typename R, typename... Args, template <typename...> class Container>
struct Memo<Container, R, std::tuple<Args...>>
The Container template argument is switched with the variadic template argument (hopefully someone understood what I just wrote). The R is just the first element in the pack.
The code compiles fine, so I guess there should be a simple explanation of why it is allowed to do.
So, why is it allowed to switch template arguments in a template specialization? Is it because a template specialization is an independent type?
The order of the template argumments in a partial template specialization doesn't matter at all. Thats because a partial template specialization is not really a template, its only an specialization of an existing template.
That means the set of template parameters of a partial template specialization are not the parameters of a template, are only the declaration of the set of generic parameters which the specialization uses/needs.
Consider an example:
// A typelist template:
template<typename... Ts>
struct type_list {};
// A metafunction for concatenating typelists (Forward declaration):
template<typename LIST1 , typename LIST2>
struct concat;
//The partial specialization of that template for typelists:
template<typename... Ts , typename... Us>
struct concat<type_list<Ts...> , type_list<Us...>>
{
using result = type_list<Ts...,Us...>;
};
In the partial specialization of the concat metafunction, what we are doing is to specialize the template for two unspecified typelists. So the template parameters of the typelits are unspecified, that is, are generic. And we need to "declare" that generic parameters before using it.
Consider the template parameters of a partial template specialization only as declarations of the generic paraeters which the specialization needs. Because there are not real template parameters, they don't have the restrictions of template parameters: Note that in the specialization I have used two variadic packs, which is not allowed in a template.
Related
I need to create a tuple-like variadic class template that takes an arbitrary number of functions, each taking a std::array as an argument. Example:
template <unsigned SZ>
using func_t = void(*)(std::array<int, SZ>);
template <unsigned SZ, func_t<SZ>... Fs>
struct FuncWrapper
{};
template <unsigned SZ, func_t<SZ> F, func_t<SZ>... Fs>
struct FuncWrapper<SZ, F, Fs...>: public FuncWrapper<SZ, Fs...>
{};
This is giving me the error
class template partial specialization is not more specialized than the
primary template
which I'm guessing might have to do with the fact that I'm passing the array size twice; once as a template argument to the FuncWrapper class, and then again as a template argument to the array. This is only happening with C++17; it works in C++14 and earlier. How can I redesign this to get the template specialization to work right?
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?
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.
This is a answer based on what caused "click" in my head when it comes to this, I am sure max66 answer is much more useful if you can follow it. If like me even after reading about this you are still strugling here is my attempt to explain this.
Do not read the first lines of templates
So this:
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 {};
becomes this:
struct is_specialization : std::false_type {};
struct is_specialization<Template<Args...>, Template> : std::true_type {};
Now things are bit simpler. We have first general case, and second special case(that is prefered by compiler when he can match the arguments).
By match I do not mean anything fancy, almost literal text matching.
So for example if we do
is_specialization<std::vector<double>, std::set>
Now compiler will try to match special case.
You can imagine it literally replacing the Template with std::vector (if he is matching on the first argument first), and then fail since he now expects Template to mean std::vector and in the second argument it is std::set.
He also replaced Args... with double but that is not so important since that is not the cause of mismatch.
Similarly compiler could have first tried to match second argument and conclude that Template must be std::set, and then he fails since first argument is not std::set<Args...>.
I consider the first lines of templates much less important and easier to understand, but for completeness let's go over them.
template <class T, template <class...> class Template>
Just means that this is a template whose second arg is template template argument, this is needed so you can pass something like std::set as second argument(note that normally this will not work, you would need to pass std::set of some type, e.g. std::set<float>).
The ugliest part is the first line of second template:
template <template <class...> class Template, class... Args>
Here again if we remember what we do in specialization code makes sense.
We have template template argument Template, and Args are needed just because we want to use template template argument as normal template argument(as first arg(Template<Args...>)).
tl;dr
Ignore first lines of template, pattern match.
Disclaimer: Like I said this is just my way of explaining this code to myself, I know that standard people would cry reading phrases like "first line" or "pattern match".
Trying to understand this bit of code using variadic templates:
template <typename... T>
struct FooGroup;
template <typename... FooTypes, typename... BarTypes>
struct TEST<FooGroup<FooTypes...>, BarGroup<BarTypes...>>
I know variadic templates allows you to pass in unknown type of unknown amount. However, this is confusing to me. Can anyone shed some light?
This code
template <typename... T>
struct FooGroup;
declare FooGroup as a struct that receive a variadic list of type template parameter.
template <typename... FooTypes, typename... BarTypes>
struct TEST<FooGroup<FooTypes...>, BarGroup<BarTypes...>>
is part of template specialization.
I suppose that TEST is declared as follows
template <typename, typename>
struct TEST;
so receiving two template types parameter.
With
template <typename... FooTypes, typename... BarTypes>
struct TEST<FooGroup<FooTypes...>, BarGroup<BarTypes...>>
you declare a partial specialization of TEST in case the first template parameter is in the form FooGroup<FooTypes...> (where FooTypes... is a variadic list of template parameters) and the second template parameter is in the form BarGroup<BarTypes...> (where I suppose BarGroup is defined almost as FooGroup and BarTypes... is another variadic list of template parameters)
Suppose the following declaration:
template <typename T> struct MyTemplate;
The following definition of the partial specialization seems to use the same letter T to refer to different types.
template <typename T> struct MyTemplate<T*> {};
For example, let's take a concrete instantiation:
MyTemplate<int *> c;
Now, consider again the above definition of the partial specialization:
template <typename T> struct MyTemplate<T*> {};
In the first part of this line (i.e. template <typename T>), T is int *. In the second part of the line (i.e. MyTemplate<T*>), T is int!
So, how is the definition of the partial specialization read?
Read it like this:
The primary template says "MyTemplate is a class template with one type parameter":
template <typename> struct MyTemplate;
The partial specialization says, "whenever there exists a type T"...
template <typename T>
... such that a specialization of MyTemplate is requested for the type T *"...
struct MyTemplate<T *>
... then use this alternative definition of the template.
You could also define explicit specializations. For example, could say "whenever the specialization is requested for type X, use this alternative definition:
template <> struct MyTemplate<X> { /* ... */ };
Note that explicit specializations of class templates define types, wheras partial specializations define templates.
To see it another way: A partial class template specialization deduces, or pattern-matches, the structure of the class template arguments:
template <typename T> struct MyTemplate<T *>
// ^^^^^^^^^^^^ ^^^^^
// This is a new template Argument passed to the original class
// template parameter
The parameter names of this new template are matched structurally against the argument of the original class template's parameters.
Examples:
MyTemplate<void>: The type parameter of the class template is void, and the primary template is used for this specialization.
MyTemplate<int *>: The type parameter is int *. There exists a type T, namely T = int, such that the requested type parameter is T *, and so the definition of the partial specialization of the template is used for this specialization.
MyTemplate<X>: The parameter type is X, and an explicit specialization has been defined for that parameter type, which is therefore used.
The correct reading of the specialisation is as follows:
template <typename T> // a *type pattern* with a *free variable* `T` follows
struct MyTemplate<T*> // `T*` is the pattern
When the template is instantiated by MyTemplate<int*>, the argument is matched against the pattern, not the type variable list. Values of the type variables are then deduced from the match.
To see this more directly, consider a template with two arguments.
template <typename T1, typename T2>
struct A;
and its specialisation
template <typename T1, typename T2>
struct A<T1*, T2*>;
Now you can write the latter as
template <typename T2, typename T1>
struct A<T1*, T2*>;
(the variable list order is reversed) and this is equivalent to the previous one. Indeed, order in the list is irrelevant. When you invoke A<int*, double*> it is deduced that T1=int, T2=double, regardless of the order of T1 and T2 in the template head.
Further, you can do this
template <typename T>
struct A<T*, T*>;
and use it in A<int*, int*>. It is now plainly clear that the type variable list has no direct correspondence with the actual template parameter list.
Note: the terms "pattern", "type variable", "type pattern matching" are not standard C++ terms. They are pretty much standard almost everywhere else though.
You have this line:
MyTemplate<int *> c;
Your confusion seems to come from assuming that the int * in < > somehow corresponds to the T in template <typename T>. It does not. In a partial specialisation (actually in every template declaration), the template parameter names are simply "free variable" (or perhaps "placeholder") names. The template arguments (int * in your case) don't directly correspond to these, they correspond to what is (or would be) in the < > following the template name.
This means that the <int *> part of the instantiation maps to the <T*> part of the partial specialisation. T is just a name introduced by the template <typename T> prefix. In the entire process, the T is int.
There is no contradiction. T should be read as T, T* is T*.
template <typename T> struct MyTemplate<T*> {};
"In the first part of this line (i.e. template <typename T>), T is int *."
No - in template <typename T> T is int, in struct MyTemplate<T*> {}; T is also int.
"Note that when a partial specialization is used, a template parameter is deduced from the specialization pattern; the template parameter is not simply the actual template argument. In particular, for Vector<Shape*>, T is Shape and not Shape*." (Stroustrup C++, 4th ed, 25.3, p. 732.)
Suppose I have a meta-function which requires both the type and its arguments. How can I do this without forcing the user of the function to provide both the type and its arguments separately?
using MyType = SomeType<T1, T2, T3>; // This is the type to pass to the functor
template <typename MyType, typename ... Args>
struct MetaFunction
{
using type = SomeOperation<MyType, Args...>;
}
// I want to be able to call my functor like this:
MetaFunction<MyType>::type;
// but with the above I have to call it like this:
MetaFunction<MyType, T1, T2, T3>::type;
How can I do this?
You can "disassemble" a class template specialization into the template itself and the template parameters by using partial spezialiation (some form of pattern-matching).
First, we define MetaFunction to be a (class) template that takes a type as its only parameter:
template <typename T>
struct MetaFunction;
Then, we define a partial specialization of this class template MetaFunction. A partial specialization is a special case of a class template. If the case applies, the partial specialization is used instead of the original (primary) template to make a class. The template "interface", i.e. the amount and kind of template arguments you can/must provide to instantiate the MetaFunction template is not changed by the existence of specializations.
A partial specialization, as opposed to an explicit specialization, is also a template, which implies that it has template parameters. The partial means that it does not specify fully for which types (sets of template arguments) it applies as a special case. Typically, partial specializations apply to subsets of the set of possible/valid arguments you can supply to the primary template.
template <template<typename...> class TT, typename ... Args>
struct MetaFunction /* not finished yet */
There are three kinds of template parameters:
Type parameters
"Value" parameters (non-type)
Template parameters
This partial specialization uses a template template-parameter. A valid argument for such a parameter must be a either a class template or an alias template.
We specify when this partial specialization shall be used by providing a "pattern":
template <template<typename...> class TT, typename ... Args>
struct MetaFunction<TT<Args...>>
{
using type = SomeOperation<TT, Args...>;
};
This partial specialization will be used if the type supplied to the MetaFunction template matches the pattern TT<Args...>, i.e., if it can be disassembled into a class/alias(*) template TT and some template type parameters Args.
(*) As T.C. correctly noted, a type produced via an alias-template is not associated with the alias-template. Therefore, you cannot deduce an alias-template from a type.