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".
Related
#include <concepts>
#include <tuple>
template<std::integral... Is>
using arithmetic_tuple = std::tuple<Is...>;
template<typename T, template<typename...> typename U>
struct is_instance_of : std::false_type {};
template<typename... Ts, template<typename...> typename U>
struct is_instance_of<U<Ts...>, U> : std::true_type {};
template<typename T>
struct is_tuple : std::false_type {};
template<typename... Ts>
struct is_tuple<std::tuple<Ts...>> : std::true_type {};
template<typename T>
struct is_arithmetic_tuple : std::false_type {};
template<typename... Ts>
struct is_arithmetic_tuple<arithmetic_tuple<Ts...>> : std::true_type {};
static_assert(is_tuple<arithmetic_tuple<int>>::value); // OK
static_assert(is_arithmetic_tuple<arithmetic_tuple<int>>::value); // OK
static_assert(is_instance_of<arithmetic_tuple<int>, arithmetic_tuple>::value); // fails
int main() {}
You can run test here: https://coliru.stacked-crooked.com/a/ce8ae52a1d4918ba
I have write a meta function is_instance_of to check a type is a instantiated type of class template.
And, I have defined a arithmetic_tuple with std::integral constraints.
is_instance_of<arithmetic_tuple<int>, arithmetic_tuple>::value is fales.
However, with more specific meta function is_tuple<arithmetic_tuple<int>>::value and is_arithmetic_tuple<arithmetic_tuple<int>>::value are true.
At a glance, at least is_arithmetic_tuple<arithmetic_tuple<int>>::value should be identical with is_instance_of<arithmetic_tuple<int>, arithmetic_tuple>::value semantically.
Why does is_instance_of test here fail?
And, Can I make it works without more specific type test such as is_tuple?
You can't deduce against an alias template. An alias template, even if written in the form you have, does not given an alias to a template itself. It is instead itself an independent template with each specialization being an alias for a type and these individual type aliases need not be in any way related through e.g. the same class template.
When the compiler comes to the point of choosing the partial specialization for is_instance_of in is_instance_of<arithmetic_tuple<int>, arithmetic_tuple>, the alias arithmetic_tuple<int> has already been resolved to the type std::tuple<int>.
Then when looking at your partial specialization which has U as the alias template arithmetic_tuple the compiler has no way of figuring out which potential Ts... would result in arithmetic_tuple<Ts...> being the same type as std::tuple<int>, because an alias template can map any list of template arguments to any type completely arbitrary. So such a deduction is not allowed. The partial specialization is considered to not match.
If you use
is_instance_of<arithmetic_tuple<int>, std::tuple>
instead, it will work as you expect, because std::tuple is the actual class template of which the type aliased by arithmetic_tuple<int> is a specialization.
The constraints on your alias template don't matter. The result will be the same without them.
Because the mapping of types via the alias is arbitrary, you can't in general write the trait how you seem to intent it. However, with the apriori assumption that the alias template does nothing more than forward the argument list, possibly constrained, to a class template, then you could do:
template<typename... Ts, template<typename...> typename U, template<typename...> typename V>
requires std::same_as<V<Ts...>, U<Ts...>>
struct is_instance_of<V<Ts...>, U> : std::true_type {};
It deduces the class template V independently of the passed template template argument for U and instead checks afterwards if the template argument list of the type would be allowed in U and result in the same type. If the assumption is not satisfied this will result in non-sense though.
I'm attempting to make an std::function alternative that supports optional arguments with defaults. I tried a few different syntactical ideas, but the most realistic seems to be a parameter pack of specialised templates that hold argument data. Here's my desired outer syntax:
Function<
void /*Return type*/,
Arg<false, int> /*Required int parameter*/,
Arg<true, bool, true> /*Optional bool parameter that defaults to true*/
> func;
I would have liked to maintain the Function<ReturnType(args)> syntax but it appears you can only put typenames in parentheses and not classes. Here's my current code:
template<bool Optional, typename Type, Type Default>
class Arg;
template<typename Type, Type Default>
class Arg<false, Type, Default> {};
template<typename Type, Type Default>
class Arg<true, Type, Default> {};
Problem 1: I can't find a way to make the "Default" parameter non-required for the false specialisation. I've tried proxy classes with a default constructor, changing the third argument to a pointer and with specifying nullptr (which isn't really ideal syntactically), and a const reference to a type (which still requires three arguments from the user side) but nothing seems to allow two arguments to be accepted with Function<false, Type>.
Problem 2: I can't find the right syntax to get a parameter pack of mixed template argument types. I've tried (obviously invalid syntax)
template<typename RetType, Arg<bool, typename Type, Type>... args>
class Function{};
and even a double/nested template but I can't make this work either.
All of this indirection really steams from the fact that you can't have multiple parameter packs in a class template so I have to find creative ways to specify optional arguments, but I will take any solution I can get to somehow making this function at compile-time and not having to construct these functions dynamically.
I'm using C++20.
To make the third template argument optional when the first argument is false, you can use a default argument with a std::enable_if:
template <bool Optional, typename T,
T Default = std::enable_if_t<!Optional, T>{}>
class Arg;
This way, Arg<false, int> is equivalent to Arg<false, int, 0>, whereas Arg<true, int> is ill-formed.
You can use generic arguments:
template <typename R, typename... Args>
class Function {
static_assert(std::conjunction_v<is_arg_v<Args>...>);
};
Where is_arg can be something as simple as
template <typename T>
struct is_arg :std::false_type {};
template <bool Optional, typename T, T Default>
struct is_arg<Arg<Optional, T, Default>> :std::true_type {};
template <typename T>
inline constexpr bool is_arg_v = is_arg<T>::value;
I want to check if a class is a template specialization of another one. What I have tried is:
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 {};
It works fine when all template parameters are type arguments but not when some are non-type arguments. For example it works with std::vector but not std::array (since the later accepts an non-type argument std::size_t).
It's important that the check is made at compile time. Also the solution must work for any template, not just vectors or arrays. That means that it can be any number of type arguments and any number of non-type arguments. For example it should work with template <class A, bool B, class C, int D, class... Args> class foo;
C++20 is a weird, weird world. Cross-checking is welcome as I'm a beginner with CTAD and not entirely sure I've covered all bases.
This solution uses SFINAE to check whether class template argument deduction (CTAD) succeeds between the requested class template and the mystery type. An additional is_same check is performed to prevent against unwanted conversions.
template <auto f>
struct is_specialization_of {
private:
template <class T>
static auto value_impl(int) -> std::is_same<T, decltype(f.template operator()<T>())>;
template <class T>
static auto value_impl(...) -> std::false_type;
public:
template <class T>
static constexpr bool value = decltype(value_impl<T>(0))::value;
};
// To replace std::declval which yields T&&
template <class T>
T declrval();
#define is_specialization_of(...) \
is_specialization_of<[]<class T>() -> decltype(__VA_ARGS__(declrval<T>())) { }>::value
// Usage
static_assert(is_specialization_of(std::array)<std::array<int, 4>>);
First caveat: Since we can't declare a parameter for the class template in any way without knowing its arguments, passing it around to where CTAD will be performed can only be done by jumping through some hoops. C++20 constexpr and template-friendly lambdas help a lot here, but the syntax is a mouthful, hence the helper macro.
Second caveat: this only works with movable types, as CTAD only works on object declarations, not reference declarations. Maybe a future proposal will allow things such as std::array &arr = t;, and then this will be fixed!
Actually fixed by remembering that C++17 has guaranteed copy-elision, which allows direct-initialization from a non-movable rvalue as is the case here!
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.)
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.