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.)
Related
Toying around with literal, non-type template parameters in c++20, I found out that g++ and clang++ disagree about the following code.
#include <algorithm>
template<size_t N>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) {
std::copy_n(str, N, value);
}
char value[N];
};
template <typename T, StringLiteral Name>
struct named{};
template <typename T>
struct is_named: std::false_type{};
template <typename T, size_t N, StringLiteral<N> Name>
struct is_named<named<T, Name>>: std::true_type{};
// This will fail with g++
static_assert(is_named<named<int, "ciao">>::value == true);
See it live on godbolt: https://godbolt.org/z/f3afjd
First and foremost, I'm not even sure I'm doing it the right way: is it that the way one matches with a generic StringLiteral<N> type, or is it not? If not, what's the right way?
And, why do the compilers disagree about it? Who's got the bug?
EDIT: found out that removing the size_t N parameter in the partial specialization makes both compilers agree, and the result is the expected one. Like this:
template <typename T, StringLiteral Name>
struct is_named<named<T, Name>>: std::true_type{};
However, I'm still curious about whether my 1st attempt is legit, by the standard, and which compiler got it wrong.
Let's focus on the partial specialization of is_named:
template <typename T, size_t N, StringLiteral<N> Name>
struct is_named<named<T, Name>>: std::true_type{};
and particularly try to answer whether it violates [temp.class.spec.match]/3 or not:
If the template arguments of a partial specialization cannot be deduced because of the structure of its template-parameter-list and the template-id, the program is ill-formed.
noting that Clang apparently does not think so, and uses the single template argument to the primary template to deduce all template arguments of the partial specialization. In this particular case these template arguments are those matching its template-parameter-list:
the template argument for the type template parameter T
the template argument for the non-type template parameter N
the template argument for the non-type template parameter Name
As per [temp.deduct.type]/4:
[...] If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails. [...]
we can break the question down into whether all three template parameters T, N and Name of the partial specialization are used in least one deduced context or not.
Starting from [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.
and [temp.deduct.type]/3 and (again) /4:
/3 A given type P can be composed from a number of other types, templates, and non-type values:
[...]
A type that is a specialization of a class template (e.g., A<int>) includes the types, templates, and non-type values referenced by the template argument list of the specialization. [...]
/4 In most cases, the types, templates, and non-type values that are used to compose P participate in template argument deduction.
We can without loss of generality consider the actual type of the template argument for the single type template parameter of the primary template (which we intend to be part fo the family of types for which partial specialization to applies), say A, as named<int, StringLiteral<5>{"ciao"}>.
The type specified in terms of the template parameters of the partial specialization, say P, is named<T, Name>.
T can be trivially deduced, matching A/P as named<int, StringLiteral<5>{"ciao"}>/named<T, Name>, to int, as T in named<T, Name> is not in a non-deduced context.
Name is similarly not in a non-deduced context, and can be deduced to StringLiteral<5>{"ciao"}.
The tricky part is N, which is not explicitly part of P, but only implicitly so via the template parameter Name. However, here we may simply apply the deduction rules recursively: Name has been deduced to StringLiteral<5>{"ciao"}, meaning we consider a new A/P pair StringLiteral<5>/StringLiteral<N>, in which N is non in a non-deducible context, and N can thus ultimately be deduced to 5.
And, why do the compilers disagree about it? Who's got the bug?
Consequently, Clang (as well as MSVC) are correct to accept your original variant, whereas GCC is wrong to reject it (rejects-valid bug).
A more minimal example which is (correctly) accepted by Clang and MSVC, and (incorrectly) rejected by GCC is:
template<int N> struct S {};
template<S s> struct U {};
template<typename> struct V { V() = delete; };
template <int N, S<N> s>
struct V<U<s>> {};
V<U<S<0>{}>> v{};
// Expected: use partial specialization #1
// GCC actual: error (rejects-valid): use of deleted function
and I have filed a bug report using this example:
Bug 99699 - Type deduction failure for deducing a non-type template parameter via another deducible structural type (class template specialization) non-type template parameter
[...] removing the size_t N parameter in the partial specialization makes both compilers agree, [...]
In your second variant, the deduction case is not as complex as the first one, and you can use a similar analysis to see that is likewise well-formed (all template parameters of the partial specialization are deducible).
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.
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.
C++14 draft n4140 reads
T shall be an enumeration type
for template <class T> struct underlying_type.
How bad is it to write
std::conditional_t<std::is_enum<T>::value, std::underlying_type_t<T>, foo>
when T can be an arbitrary type? Will I step onto an UB and will the compiler delete my $HOME (because language lawyers say "anything can happen under an UB") ?
Will I step onto an UB [...]
Technically, yes. But practically, it just won't compile for non-enumeration types. When you write:
std::conditional_t<std::is_enum<T>::value, std::underlying_type_t<T>, foo>;
^^^^^^^^^^^^^^^^^^^^^^^^^
That template parameter must be evaluated before the conditional template can be instantiated. This is equivalent to all function arguments having to be invoked before the body of the function begins. For non-enumerated types, underlying_type<T> is incomplete (sure it's specified as undefined in the standard but let's be reasonable), so there is no underlying_type_t. So the instantiation fails.
What you need to do is delay the instantiation in that case:
template <class T> struct tag { using type = T; };
typename std::conditional_t<
std::is_enum<T>::value,
std::underlying_type<T>,
tag<foo>>::type;
Now, our conditional instead of selecting types is selecting a metafunction! underlying_type<T>::type will only be instantiated for T being an enum. We additionally have to wrap foo to turn it into a metafunction.
This is a common pattern and was a special thing in Boost.MPL called eval_if, which would look like:
template <bool B, class T, class F>
using eval_if_t = typename std::conditional_t<B, T, F>::type;
Note that we're both using conditional_t and ::type.
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