What exactly happens in this template metaprogramming snippet? - c++

template <typename T, typename = void>
struct IsIterable : std::false_type {};
template <typename T>
struct IsIterable<
T,
decltype(
std::begin(std::declval<std::add_lvalue_reference_t<T>>()),
std::end(std::declval<std::add_lvalue_reference_t<T>>()),
void()
)
> : std::true_type {};
I thought I understood this.
The first IsIterable is a class template with a default template argument for the second parameter. It obviously is applicable for every type.
The second part is a partial template specialization of IsIterable. Thanks to SFINAE, it is only chosen when T has the member functions begin() and end().
Here is my first question I just came up with actually: the base template has a default argument, so this means it still has two template parameters. The partial specialization only has one template parameter. So does this mean that "one template parameter" always gets choosen before "two template parameters, one default"?
Also, do I understand it correctly that the first "base" template inherits from false_type, and the partial template specialization "adds" another inheritance level? (so does the partial specializations inheritance hierarchy look like this: false_type > true_type > IsIterable, where the definitions of false_type are hidden by true_type?)
Now onto my actual question. Why does the decltype expression have to evaluate to void? I thought that it wouldn't matter and I could write
template <typename T>
struct IsIterable<
T,
decltype(
std::begin(std::declval<std::add_lvalue_reference_t<T>>()),
std::end(std::declval<std::add_lvalue_reference_t<T>>()),
bool() // **** change here ****
)
> : std::true_type {};
as well. But this makes it so that the value of IsIterable is always false! Why is, when I change the partial specialization from void to bool, always the first template chosen?

So does this mean that "one template parameter" always gets choosen before "two template parameters, one default"?
No. A partial specialization is used when an instantiation matches its template argument list, and when it is "more specialized" than any other partial specializations that match (which is not relevant here, because there are no other partial specializations declared).
When you instantiate the template as IsIterable<Foo> the default template argument is used, so the instantiation is IsIterable<Foo, void>. If Foo has begin() and end() members then IsIterable<Foo, void> matches the partial specialization, which is more specialized than the primary template. When Foo doesn't have begin() and end() members the partial specialization is not usable, because the expression decltype(std::declval<T>().begin(), std::declval<T>().end(), void()) causes a subsitution failure when Foo is substituted in place of T.
Also, do I understand it correctly that the first "base" template inherits from false_type, and the partial template specialization "adds" another inheritance level?
No. There is no implicit inheritance relationship between a primary template and a specialization. (If you stop calling it a "base" template and call it by its proper name, the primary template, maybe you won't think there is any kind of "base" class relationship.)
When a partial specialization (or an explicit specialization) is used it is used instead of the primary template, not in addition to it.
Why does the decltype expression have to evaluate to void?
Because that's the type of the default template argument on the primary template. You are not supposed to provide an argument for the second template parameter, it's supposed to use the default, and that is void.
Why is, when I change the partial specialization from void to bool, always the first template chosen?
Because when you write IsIterable<Foo> that uses the default template argument, so is equivalent to IsIterable<Foo, void> which can never match a partial specialization of the form IsIterable<T, bool> because void is not the same type as bool!
If you made the partial specialization use bool() instead then you would have to write IsIterable<Foo, bool> for the partial specialization to match. You could do that ... but it's not how the trait is designed to be used. Alternatively you could change the default template argument on the primary template to be bool as well, but void is the idiomatic choice, since the specific type doesn't matter, all that matters is the default matches the specialization. You're meant to provide only one template argument and let the default be used for the second. And the partial specialization can only match if its second template argument is the same as the default template argument.

The second part is a partial template specialization of IsIterable. Thanks to SFINAE, it is only chosen when T has the member functions begin() and end().
That's not all there is. The expression t.begin(), t.end(), void() for a t of type T must also have a valid type and that must be void for the specialization to be selected. (That's what the decltype achieves.)
Here is my first question I just came up with actually: the base template has a default argument, so this means it still has two template parameters. The partial specialization only has one template parameter. So does this mean that “one template parameter” always gets choosen before “two template parameters, one default”?
Every valid instantiation of the specialization is also a valid instantiation of the base case (it's an implication) so the specialization (if viable) is a better match.
Also, do I understand it correctly that the first “base” template inherits from false_type, and the partial template specialization “adds” another inheritance level? (so does the partial specializations inheritance hierarchy look like this: false_type > true_type > IsIterable, where the definitions of false_type are hidden by true_type?)
No, they are completely unrelated types. A specialization of a class template does not inherit implicitly from the general case.
If you want to see this live, try adding a large non-static data member (eg int[100]) to the general case and then compare the sizeof the instantiated types. If the special case is smaller, it cannot possibly be derived from the general case.
Now onto my actual question. Why does the decltype expression have to evaluate to void?
It doesn't have to but in order to make this work, you'll also have to change the default for the base case from void to bool or whatever. Be aware, however, that an overloaded operator, could cause you strange surprises if you pick anything but void.
The technique was nicely explained in a talk Walter Brown gave at CppCon 2014. If you have two hours sparse, I strongly recommend you watch the recording of his talk:
Walter E. Brown, Modern Template Metaprogramming: A Compendium, Part I. CppCon 2014.
Walter E. Brown, Modern Template Metaprogramming: A Compendium, Part II. CppCon 2014

Related

How does enable_if help select specializations of a class template?

I have a basic grasp of SFINAE, and I think I understand many of the examples of how std::enable_if exploits it to select function template specializations, but I'm having a hard time wrapping my head around how it works for class templates.
The following example is from cppreference.com's explanation of std::enable_if:
template<class T, class Enable = void>
class A {}; // primary template
template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
}; // specialization for floating point types
I'm having trouble understanding how using std::enable_if in this way helps select the specialization. (I don't doubt that it does.)
When the compiler sees a declaration like A<float> specialized;, it will see two possible template instantiations that fit:
The "primary template" A<T, Enable> where T is the type float and Enable is the type void (because of the default value).
The specialization A<T, void> where T is the type float and void is the result of the expression with the enable_if.
Aren't those ambiguous? Both effectively result in A<T, void>, so why is the specialization chosen?
In a different case, like A<int> primary;, the options to the compiler seem to be:
The primary, A<T, Enable>, where T is the type int and Enable is the type void.
The specialization, A<T, ?>, where T is the type int and the ? represents where I'm completely lost. In this case, the enable_if condition is false, so it doesn't define type, which leaves you with A<int, typename >. Isn't that a syntax error? Even in the face of SFINAE?
From the reference on partial specialization of class templates:
When a class or variable (since C++14) template is instantiated, and there are partial specializations available, the compiler has to decide if the primary template is going to be used or one of its partial specializations.
If only one specialization matches the template arguments, that specialization is used
In this case, if the 2nd argument of the specialization is well-formed, it is chosen, precisely because it is a specialization, and not the primary template.
In case the 2nd template argument is not well-formed, then SFINAE kicks in. In particular:
When substituting the explicitly specified or deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error.
and
The following type errors are SFINAE errors:
attempting to use a member of a type, where
the type does not contain the specified member
How this is done, i.e. how exactly the compiler discards the specialization, instead of giving an error, is not specified; the compiler is just required to do the right thing.
Aren't those ambiguous? Both effectively result in A<T, void>, so why is the specialization chosen?
No, the specialization is more specialized than the primary template, because it requires the second parameter to be void (assuming the enable_if condition is true), while the primary template doesn't restrict it.
The specialization, A<T, ?>, where T is the type int and the ? represents where I'm completely lost. In this case, the enable_if condition is false, so it doesn't define type, which leaves you with A<int, typename >. Isn't that a syntax error? Even in the face of SFINAE?
Exactly, the second argument in the specialization turns out invalid. But this is a "soft" error, which is detected by SFINAE and makes the compiler discard the specialization. (I don't think a compiler would textually replace enable_if_t<...>::type with an empty string and then analyze A<int, typename >; more likely it would discard the specialization as soon as it notices the lack of ::type in enable_if.)

c++ is_member_pointer implementation

In c++ std library, is_member_pointer is implemented as
template<typename _Tp>
struct __is_member_pointer_helper
: public false_type { };
template<typename _Tp, typename _Cp>
struct __is_member_pointer_helper<_Tp _Cp::*>
: public true_type { };
/// is_member_pointer
template<typename _Tp>
struct is_member_pointer
: public __is_member_pointer_helper<typename remove_cv<_Tp>::type>::type
{ };
Can someone explain that how the _Cp is deduced? It works like a magic.
The type of a pointer-to-member is Type Class::*, where Type is the object type or function type being pointed to. If you provide the template an int C::*, for example, the compiler can simply deduce the class type by examining the type of the pointer-to-member and seeing the class type is C. It would also deduce the pointed-to type as int the same way. It works very similarly to how we humans would do it. In general, we call this technique pattern matching, which you might be familiar with from regular expressions.
Formally:
[temp.deduct.type]/3.2:
A pointer-to-member type includes the type of the class object pointed to and the type of the member pointed to.
[temp.deduct.type]/8:
A template type argument T, a template template argument TT or a template non-type argument i can be deduced if P and A have one of the following forms:
[snip]
T T::*
[snip]
Where per [temp.deduct.type]/1:
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
Some years ago I basically had the same misunderstanding about C++ template specialization that, apparently, you are having now.
The other answers are great, but I don't think they would have really helped me back then understanding what's going on. So, assuming that you are suffering from the same misunderstanding that I did, let me try to explain how I finally got my thoughts right:
Back then, my intuitive understanding erroneously told me that the term "specialization" means that the "specialized template" should somehow have less template arguments than the "original template". This assumption was driven by the fact that almost every tutorial code that tries to explain how specialization works starts with an example like
template <class T> // <-- one parameter
class MyClass { ... };
template <> // <-- zero parameters
class MyClass<int> { ... };
Now, your example of is_member_pointer is one counterexample showing that this is not true at all.
My entire misunderstanding started with using wrong terminology. You may have noticed that above I put quotes around "specialized template" and "original template"? I did this because it's wrong, but those were the words that I was using back then.
True is, that there is only one template. It's wrong to say that there are two templates, an original one and a specialized one. In my example
template <class T>
class MyClass { ... };
is the template, while
template <>
class MyClass<int> { ... };
is a specialization of that same template.
What makes it a specialization, is the use of <int> behind the class name. That's it!
This is another valid specialization of that same template:
template <class... Types>
struct many_to_one { ... };
template <class A, class B, class C, class D> // <-- four parameters, could be even more
class MyClass<many_to_one<A, B, C, D>> { ... };
As you can see, the specialization has way more template parameters than the actual template. And that's perfectly valid as long as the number of specialization types (one type in this example, namely many_to_one<A, B, C, D>) matches the number of template parameters of the actual template.
Now, what does the compiler do, if you use MyClass<int> anywhere, e.g. for declaring a variable of that type?
It has a look at the template and all specializations of it.
First thing to note: As in this example there is only one template parameter, something like MyClass<int, double, short, float> cannot compile, even though there is a specialization with four parameters! But those four parameters apply to the specialization, not to the template.
When the compiler walks through all the specializations and it finds
template <class A, class B, class C, class D>
class MyClass<many_to_one<A, B, C, D>> { ... };
it has to ask itself "are there any types A, B, C, D so that the given type (int) is equal to the specialization type many_to_one<A, B, C, D>? The answer is No, so it moves on to the next specialization:
template <>
class MyClass<int> { ... };
Again, it asks itself "are there any types <empty list here> so that the given type (int) is equal to the specialization type int? Obviously, yes! Since there are no even better matching specializations, it chooses this one.
If instead you have e.g. MyClass<double>, both questions for the two specializations yield No as the answer, so the "basic" template will be chosen.
So, to finally answer your original question: If you write e.g.
std::is_member_pointer<decltype(&std::string::size)>
the compiler looks at the specialization and sees "Oh, look. If I put _Tp = size_t and _Cp = std::string then the given type decltype(&std::string::size) is equal to the specialization type _Tp _Cp::*, so I pick that specialization (that happens to inherit from std::true_type)".
But if you write e.g.
std::is_member_pointer<int>
then it cannot find any types for _Tp and _Cp to make _Tp _Cp::* equal to int, so it discards that specialization and chooses the "basic" template (that happens to inherit from std::false_type).
There is nothing magical about it. It is simple specialization, and _Cp is deduced to containing class, whenever the template is instantiated for the class member.
It is an application of general case of selecting best available specialization.

Partial specialization and the need for std::void_t<>

One for the language lawyers....
I'm playing around with SFINAE and TMP, trying to get a deeper understanding.
Consider the following code, a naive implementation of std::is_default_constructible
#include <type_traits>
template <typename T, typename = void> struct is_default_constructable : std::false_type {};
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
class NC { NC(int); }; // Not default constructable
#include <iostream>
int main(int, char **)
{
std::cout << "int is_default_constructible? " << is_default_constructable<int>() << std::endl;
std::cout << "NC is_default_constructible? " << is_default_constructable<NC>() << std::endl;
}
This compiles fine, but doesn't actually work, it returns false for all types.
For the NC case, this is as I'd expect, T() is not well-formed so that specialization is discarded due to SFINAE and the primary template (false_type) is used. But for the int case, I'd expect the specialization to be used as decltype(T()) is valid and equivalent to T.
If, based on the actual code in <type_traits>, I change the specialization to
template <typename T> using wrap = void;
template <typename T> struct is_default_constructable<T, wrap<decltype(T())> > : std::true_type {};
(i.e. wrap the second template parameter in a mockup of std::void_t<> which forces the second type to be void), this works as expected.
Even more curious, variations of this scheme using types other than void as the default type in the primary template or wrap<> also fail, unless the two types are the same.
Can someone explain why the type of wrap<> and the second template argument default type need to be the same in order for the specialization to be selected?
(I'm using "g++ -Wall --std=c++17" with g++ version 6.3, but I think this is not compiler-related.)
This is not a consequence of SFINAE or partial specialization ordering, but due to the use of default template parameters. Informally, the reason is that the application of default template parameters happens before the search for template definitions, including possible specializations.
So in the above case, the code that says is_default_constructable<int> is actually requesting to instantiate a template is_default_constructable<int, void> after applying the default second parameter. Then possible definitions are considered.
The "primary" template definition clearly matches and is included.
The given partial specialization
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
is actually defining is_default_constructable<int, int> which does not match the requested is_default_constructable<int, void> so the specialization is ignored, even if the substitution succeeds.
This leaves the primary definition (inheriting false_type) as the only viable definition so it is chosen.
When the specialization has wrap<> (or std::void_t<>) to force the second arg to a void, the specialization is defining is_default_constructable<int, void> which matches the request. This definition (asuming the substitution succeeds, i.e. T() is well formed) is more specialized than the primary definition (according to super-complicated rules for ordering specializations), so it is chosen.
As an aside, the above naive implementations probably don't work as expected when T is a reference type or other corner cases, which is a good reason to use the standard library versions of all this. Them standards committee people are way smarter than I am and have already thought of all these things.
This answer and this answer to somewhat-related questions have more detailed information that set me right.
And, yes, I can't spell constructible, assuming that's even a word. Which is another good reason to use the standard library.

Are all valid templates instantiated?

When I have multiple templates that could be instantiated, what happens?
template <typename T> void foo(T*);
template <typename T> void foo(T**);
template void foo(int**);
template <typename T> void foo(T*) { printf("called foo(T*)\n"); }
template <typename T> void foo(T**){ printf("called foo(T**)\n"); }
When we need to instantiate a foo(int**), either of the instantiations would work, one with T=int* and the other with T=int**. In practice, the object file only contains one, the T** one. On the other hand, if I put a static_assert(false) into the unused one, the compilation fails. Does that mean that it is instantiated?
It turns out that the T** is the one that I want to use in this case. But even if both were instantiated, overload resolution would pick the T** one, so this works for me either way.
Now in my actual code, I request two instantiations:
template void foo(int*);
template void foo(int**);
That instantiates foo<int>(int**) and foo<int>(int*), so I get both templates. The latter is forced, since template <typename T> foo(T**) doesn't match. But I don't understand the rules to know why I don't get foo<int*>(int**) for the former. Or both foo<int*>(int**) as well as foo<int>(int**).
And yes, I realize I could do template void foo<int*>(int**) if I needed to force it.
All this makes me realize I don't really understand what happens when the compiler sees a call that requires a template instantiation. Does it scan through templates until it finds one that works, then stop? Or does it instantiate all valid options (and then something discards the unneeded ones from the object file)?
Template argument deduction is performed to figure out what function template specialization is named by template void foo(int**);; it selects the T** one after considering partial ordering. See [temp.deduct.decl] and sections linked therein. That's roughly the same process used to pick the function template to call during overload resolution (with some minor differences).
There's no mechanism for instantiating everything that matches. If the compiler can't select a unique function template specialization, the program is simply ill-formed.
On the other hand, if I put a static_assert(false) into the unused one, the compilation fails. Does that mean that it is instantiated?
That's just [temp.res]/8 at work, as usual. Code that is ill-formed in a way that doesn't depend on a template parameter may be diagnosed immediately without instantiation.
if I put a static_assert(false) into the unused one, the compilation fails. Does that mean that it is instantiated?
No. The compiler does some basic checks on a template definition. Everything in the definition must be valid C++, but certain things which depend on a template parameter cannot be checked until an actual instantiation, when the template arguments are known. Since static_assert(false); does not depend on any template parameters, it always causes an error, and a compiler is allowed to note the error even when the template is never instantiated.
If you really want a template that should never be instantiated, the usual way is to use =delete for a function template, or leave a class template undefined.
But I don't understand the rules to know why I don't get foo<int*>(int**) for the former.
In most contexts that name a specialization of a function template, there's a rule that any template which is "more specialized" than all other viable overloads is the one that gets used. The exact definition of "more specialized" is a bit tricky, but the basic idea is that if any argument list that could be taken by a function template "A" could also be taken by a function template "B" but not vice versa, then "A" is more specialized. In your example
template <typename T> void foo(T*); // #1
template <typename T> void foo(T**); // #2
if an argument arg has type U** for any type U so that template argument deduction for template #2 for foo(arg) can succeed with T=U, we can see that template argument deduction for template #1 for foo(arg) can also succeed with T=U*. On the other hand, it's obviously possible that another argument arg2 can mean that template argument for foo(arg2) can succeed for template #1 but fail for template #2, for example if arg2 has type int*. So function template #2 is more specialized than function template #1. This means an expression like foo(arg), whenever type deduction for both succeeds (and there are no other viable overloads involved), means a use of template #2.
The same "more specialized" rule applies in your explicit instantiation
template void foo(int**);
Much like with a function call expression, the compiler will use template argument deduction to see whether the declaration you gave matches each function template. In this case both succeed, but since template #2 is more specialized, the declaration is interpreted as a specialization of #2 and not #1.
All this makes me realize I don't really understand what happens when the compiler sees a call that requires a template instantiation. Does it scan through templates until it finds one that works, then stop? Or does it instantiate all valid options (and then something discards the unneeded ones from the object file)?
The rough sequence for most uses of the name of an overloaded function template is:
Do name lookup to determine the list of function templates and functions to be considered.
For each function template in the list, attempt template argument deduction. If template argument deduction fails, or if substituting any template argument into the function type fails, ignore that template from here on. If successful, this results in a template argument for each template parameter, and one specific function type for the specialization.
Do overload resolution in mostly the same way as for non-templates, on the set of non-template functions initially looked up combined with the set of function template specializations determined above. But if two candidate functions cannot be ordered to say one is a better overload than the other just based on the parameter types and implicit conversions involved, then a non-template function beats a function template specialization, and a specialization from a more specialized function template beats a specialization from a less specialized function template.
Note that during this process, the function types but not the definitions of the function template specializations are instantiated.

Clarification on partial template specialisation

This question seeks clarification on a section of the following documentation on partial template specialisation:
partial template specialization
My question pertains to the following text under the heading Members of partial initialization:
If a class template is a member of another class template, and it has
partial specializations, these specializations are members of the
enclosing class template. If the enclosing template is instantiated,
the declarations of each member partial specialization is instantiated
as well (the same way declarations, but not definitions, of all other
members of a template are instantiated)
If the primary member template is explicitly (fully) specialized for a
given (implicit) specialization of the enclosing class template, the
partial specializations of the member template are ignored for this
specialization of the enclosing class template.
If a partial specialization of the member template is explicitly
specialized for a given (implicit) specialization of the enclosing
class template, the primary member template and its other partial
specializations are still considered for this specialization of the
enclosing class template.
The example section demonstrating above mentions the following:
template<class T> struct A { // enclosing class template
template<class T2>
struct B {}; // primary member template
template<class T2>
struct B<T2*> {}; // partial specialization of member template
};
template<>
template<class T2>
struct A<short>::B {}; // full specialization of primary member template
// (will ignore the partial)
A<char>::B<int*> abcip; // uses partial specialization T2=int
A<short>::B<int*> absip; // uses full specialization of the primary (ignores partial)
A<char>::B<int> abci; // uses primary
I don't understand the distinction between the three cases above, which warrant a different treatment in each case, based on the text reproduced above.
Can anyone provide a simple explanation?
Since I am not sure that i got your question correctly, so please be indulgent. I assume that you want to know the difference and the reason for the behavior in your presented instantiations.
First you need to know how the compiler chooses which template specialization to use. There is a nice explanation here, but it mostly breaks down to: The compiler chooses always the template most restricted/specialized template specialization.
So now, lets look at the first instantiation you do there:
A<char>::B<int*> abcip;
Since there exists no full specialization for A<char>::B, we look into the general definition of A and find two matching template specializations for B<int*>.
B<T2> with T2=int* and B<T2*> with T2=int.
Since B<T2*> is the more restricted one, we choose this one.
Lets now look at the second instantiation:
A<short>::B<int*> absip;
Paragraph 2 of your quoted text now applies, since there exists a full specialisation of the primary member template A<short>::B. The partial specialization A<T>::B<T2*> will not be considered. This makes sense due to the fact that A<short>::B<T2> is more specialized than A<T>::B<T2*>. Things would change if we were to add the following to your code:
template<>
template<class T2>
struct A<short>::B<T2*> {};
Since this is even more specialized, it would be the chosen template for this instantiation.
The last instantiation simply chooses the primary member template A<T>::B<T2>, because this is the only matching type.