I have the following code:
#include <iostream>
template <class T, typename U = void> class A;
template <class T>
class C
{
public:
typedef T Var_t;
};
template <class T>
class B : public C<T>
{
};
template <class T>
class A<B<T>>
{
public:
A() { std::cout << "Here." << std::endl; }
};
template <class T>
class A<T, typename std::enable_if<
std::is_base_of<C<typename T::Var_t>, T>::value>
::type>
{
public:
A() { std::cout << "There." << std::endl;}
};
int main()
{
A<B<int>> a;
return 0;
}
When the compiler tries to instantiate the second partial specialization with the parameter B<int>, std::is_base_of<C<int>, B<int>>::value is true, and therefore the std::enable_if<...>::type returns void (the default type if one isn't specified). This causes an "ambiguous partial specialization" error as the compiler can't decide between the first and second partial specializations. So far, so good. However, when I replace the code within the std::enable_if to simply be true (i.e., the second partial specialization is just template <class T> class A<T, typename std::enable_if<true>::type>), the code compiles and runs. It outputs "Here", indicating the first specialization was chosen.
My question is: If they both evaluate to void in the end, why does the behavior of std::enable_if<true>::type differ to that of std::enable_if<std::is_base_of<...>::value>::type?
This behavior has been tested and verified on Ideone here.
In the std::enable_if<true>::type case your code defines two specialisations of the class A namely:
A<B<T>, void>
A<T, std::enable_if<true>::type>.
Those two specialisations are quite distinct from each other. The first specialisation is narrowly focused on the type B<T> while the second specialisation is more general fitting any type at all. Also, in the second specialisation the std::enable_if expression does not depend on T in any way.
For any declaration A<X> a; the type X will either match B<something> or not. If it matches B<something> then the first specialisation will be used because it is "more specialised". If X does not match B<something> then the second, more general specialisation will be used. Either way you don't get the ambiguous error.
For more details see the discussion of Partial Ordering in partial template specialization
Now let's consider the std::enable_if<std::is_base_of<...>::value>::type case.
You still have two specialisations but the second specialisation is now conditional on the enable_if which in turn depends on the parameter T.
A<B<T>, void>
A<T, std::enable_if<...>>.
The type B<int> now matches both specialisations (to some equal extent). Obviously it matches the A<B<T>>, void> specialisation but it also matches the A<T, std::enable_if...>> specialisation because B<int> is a type which satisfies the conditions imposed by the std::enable_if expression.
That gives you two equally valid specialisations that are candidates for your declaration of the variable a and so you get the "ambiguous partial specialization" error.
It might help make all this a bit more concrete if you added two more declarations to main
A<C<int>> x;
A<int> y;
In the std::enable_if<true> case this will compile and both declarations will call the "there" constructor.
In the more complex case the declaration of x will compile and invoke the "there" constructor but the declaration of y will get a compiler error.
There is no int::Var_t so the std::enable_if expression will get a substitution failure and SFINAE will hide that specialisation. That means there won't be any specialisation that fits int and you'll get the error aggregate ‘A<int> y’ has incomplete type and cannot be defined
Related
I know that a C++ compiler picks a template specialization in preference to the primary template:
template<class T, class Enable = void>
class A {}; // primary template
template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, void>> {
}; // specialization for floating point types
However, I don't understand why the selection fails when a type other than void is used as argument to enable_if:
template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, int>> {
}; // specialization for floating point types
The compiler would surely "see" class A<T, int>.
So it certainly can't be SFINAE, nevertheless the primary template is preferred to the specialization.
This question arises from a context where a custom type detection machinery could be used instead of enable_if, e.g. an SFINAE friendly type extractor like common_type.
When you have
A<some_floating_point_type> some_name;
The template parameters are some_floating_point_type and the implicit void. When the compiler instantiates
template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, int>> {
}; // specialization for floating point types
it would get A<some_floating_point_type, int>, which does not match the A<some_floating_point_type, void> type that some_name has. Because of that, the specialization is ignored and you get the primary template. You can verify this by trying to create a A<some_floating_point_type, int> and you'll get that the specialization was picked.
I find it helpful to think of a specialization as an alternate recipe. First the template arguments are deduced, and then if they match any of the specializations, then we switch to using that alternate recipe. If not, then the original recipe is used.
Specializations are irrelevant until the compiler knows which types it is going to use for the primary template.
When you write A<double>, then the compiler looks only at the primary template and sees that you actually mean A<double,void>.
And only then it is looking for specializations. Now, when your specialization is for A<double,int>, then it is not suitable because you asked for A<double,void>.
In simple terms, this
template<class T, class Enable = void>
class A {};
says "A is a template, it has two arguments, the second has a default that is void". Thats the primary template. When you explicitly provide only one parameter then the second argument is void.
Now the specialization:
template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, int>> {
};
This doesn't change the above. A is still a template with 2 arguments and when the second one is not specified then it is void. In simple terms it either means "substituting T in std::enable_if_t<std::is_floating_point_v<T> is a failure, because the type alias does not exist" when the condition is false, SFINAE kicks in and the specialization is ignored. When the condition is true: "Whenever A is instantiated with arguments T (some type) and int then use this definition".
When you instantiate A<int> then the second template argument is void. The specialization does not change that. That is A<int> is actually A<int,void>. It does not match the specialiazation.
I have read What does template's implicit specialization mean? and its answers, but I am still not satisfied that I understand this part of Partial template specialization from cppreference.com:
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....
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
Questions:
The comments say that the line template<> template<classT2> struct A<short>::B {}; is a "full specialization of primary member template". The primary member template is identified in the comments as the structure B. How can the line be a specialization of B when it is A that is being specialized by the substitution of short for class T?
How can the line be a "full" specialization of B when the template parameter T2 is left unspecified?
The comments and the accompanying text indicate that "explicit specialization" and "full specialization" are synonymous terms. If the line of code quoted above is the explicit specialization of B, where is the implicit specialization of A?
A member of a class template can be explicitly specialized even if it is not a template:
template<int I>
struct X {
int f() {return I;}
void g() {}
};
template<>
int X<0>::f() {return -1;}
This is equivalent to specializing the whole class template but with other members duplicated from the relevant primary template or partial specialization:
template<>
struct X<0> {
int f() {return -1;}
void g() {}
};
(Recall that such a spelled-out specialization is under no obligation to declare g at all or as a function.) Since you don’t actually write this, it’s still considered to be an implicit instantiation of X as a whole with the given alteration.
This is why your specialization of A<T>::B provides the template argument for A and not for B; it is replacing the template A<T>::B with another template (that happens to have the same template parameter list). I would say the word “primary” is misleading here: it is the whole template that is replaced, which is why the partial specialization is ignored for A<short> thereafter.
This question already has answers here:
How does `void_t` work
(3 answers)
Closed 4 years ago.
I have a program that is as follows. There is a base template struct X and a partial specialisation with SFINAE.
template <typename T, typename U = void>
struct X{
X() {
std::cout << "in 1" << std::endl;
};
};
template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>> > {
X() {
std::cout << "in 2" << std::endl;
};
};
int main() {
X<int> x;
}
When running the program in 2 is printed.
Why is it that the second specialization is chosen over the first since both of them effectively declare a struct X<int, void>. What makes std::enable_if_t<std::is_integral_v<T>> more specialized than a default template type argument as shown in the base template?
Why does the default type argument of the base template have to be the same as the type defined by the partial specialization for the partial specialization to be called and in 2 to be printed.
Why does changing to std::enable_if_t<std::is_integral_v<T>, bool> cause the base template in 1 to be called?
The answers to your questions lie in Template Partial Ordering. This is the mechanism the compiler uses to determine which template is the best fit (be it a function template overload, or in your case, a class template specialization).
In brief, your generic template implementation has 2 parameters T and U, whereas your SFINAE specialization have only the T parameter, while the second is deduced from T. It is therefore more specialized than the general case and in the end, when you refer to X<int, void>, the specialization is chosen.
Now question 2. Suppose we replace the enable_if parameter with bool instead of void. Now our specialization will be X<int, bool> instead of X<int, void>, so when you refer to X<int>, i.e. X<int, void>, it doesn't match the specialization anymore because those are 2 different types.
1) [...] What makes std::enable_if_t> more specialised than a default template type argument as shown in the base template?
So do you know that, if two template match, the more specialized is selected.
Well... the second one is more specialized because if X matches the specialization (so if X is an integral type), it's that matches also the generic version.
But exist couples of types (by example: std::string, void) that matches the generic version and doesn't matches the specialization.
So the specialization is more specialized that the generic version, so is preferred when both template match.
Why does the default type argument of the base template have to be the same as the type defined by the partial specialisation for the partial specialisation to be called and in 2 to be printed. Why does changing to std::enable_if_t, bool> cause the base template in 1 to be called?
You have to understand how works the trick of the default type value.
You have that the generic version that is
template <typename T, typename U = void>
struct X;
so writing X<int> x, you're writing X<int, void> x; and surely matches the generic version.
The specialization is
template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>>>;
Question: X<int, void> matches X< T, std::enable_if_t<std::is_integral_v<T>>> ?
Answer: yes, because int is integral, so std::enable_if_t<std::is_integral_v<T>> is substituted with void.
Suppose now that the generic specialization become
template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>, bool>>
We have that X<int> x is again X<int, void> x and matches again the generic version.
Question: X<int, void> matches also X< T, std::enable_if_t<std::is_integral_v<T>, bool>> ?
Answer: no, because std::enable_if_t<std::is_integral_v<T>, bool> become bool and X<int, void> doesn't matches X<int, bool>
So the generic version is the only one that matches and is selected.
I am trying to do a simple partial template specialization, but I get errors on g++4.4.7, g++4.8.5, clang++3.8.0. Whenever I mention compiler(s) error, I mean the output of all of these, as they always agree.
I am using C++03, compiling without any option.
The code:
#include <iostream>
template <typename T, typename X, typename G>
struct A {};
template <typename T, typename X>
struct A<T, X, void> { A() : n(1) {} X n; T b; };
template <typename X>
struct A<X, void, void> { A() : n(2) {} X n; };
int main() {
A<int, float> one;
A<int> two;
std::cout << one.n << " | " << two.n << "\n";
return 0;
}
Question 1: This code fails to compile. The compilers say that A<int, float> and A<int> are wrong as A requires 3 templates parameters. Why?
If I change the original declaration to
template <typename T, typename X = void, typename G = void>
struct A {};
The code compiles and the output is: 1 | 2.
What happens is that the compiler in a first step matches one and two type to the not specialized A, but then it correctly decides to use the code of the partially specialized class one would expect it to use. But it should not need the defaults.
I then decide to change the last partial specialization switching the first and second parameter:
template <typename X>
struct A<void, X, void> { A() : n(2) {} X n; };
I would expect this to change nothing, but the compilers disagree. The clearest output between the 3 is here reported:
a.cpp:7:40: error: field has incomplete type 'void'
struct A<T, X, void> { A() : n(1) {} X n; T b; };
^
a.cpp:14:10: note: in instantiation of template class 'A<int, void, void>' requested here
A<int> two;
^
1 error generated.
Question 2: Why are the compilers considering the two variable an instance of the partial specialization of A that specializes only one argument?
Note that is the "2nd matching", because if I only use 1 default template argument, the compiler will go back to complaining about the fact that 3 template parameters are needed.
Thanks.
Question 1: This code fails to compile. The compilers say that A<int, float> and A<int> are wrong as A requires 3 templates parameters. Why?
Because A requires 3 template parameters. You declared A as:
template <typename T, typename X, typename G>
struct A {};
There is no two- or one-template parameter version of A. There are versions specialized on some of the types being void, but that's still a parameter - not an absence of parameter.
When you add the defaults, then A<int, float> evaluates as A<int, float, void>, which is a valid instantiation - and picks the specialization which sets n to 1.
You're misunderstanding how specialization works. Specialization doesn't change the number of template parameters. It's just a way of adding special functionality depending on what the template parameters end up being.
Question 2: Why are the compilers considering the two variable an instance of the partial specialization of A that specializes only one argument?
We have three choices
template <T, X, G> struct A; // the primary
template <T, X, void> struct A; // (1)
template <void, X, void> struct A; // (2)
When we instantiate A<int>, that is the same as A<int, void, void> when we add in the default parameters. That does not match (2) - because that one requires the first parameter to be void and yours is int. (1) is a better match than the primary since it's more specialized. But then, (1) has a member of type X and in this case X is deduced as void (from the default parameter), and that's not allowed.
This is from the C++ Standard Library xutility header that ships with VS2012.
template<class _Elem1,
class _Elem2>
struct _Ptr_cat_helper
{ // determines pointer category, nonscalar by default
typedef _Nonscalar_ptr_iterator_tag type;
};
template<class _Elem>
struct _Ptr_cat_helper<_Elem, _Elem>
{ // determines pointer category, common type
typedef typename _If<is_scalar<_Elem>::value,
_Scalar_ptr_iterator_tag,
_Nonscalar_ptr_iterator_tag>::type type;
};
Specifically what is the nature of the second _Ptr_cat_helper declaration? The angle brackets after the declarator _Ptr_cat_helper make it look like a specialization. But instead of specifying full or partial types by which to specialize the template it instead just repeats the template argument multiple times.
I don't think I've seen that before. What is it?
UPDATE
We are all clear that the specialization applies to an instantiation of the template where both template arguments are of the same type, but I'm not clear on whether this constitutes a full or a partial specialization, or why.
I thought a specialization was a full specialization when all the template arguments are either explicitly supplied or are supplied by default arguments, and are used exactly as supplied to instantiate the template, and that conversely a specialization was partial either, if not all the template parameters were required due to the specialization supplying one or more (but not all) of them, and/or if the template arguments were used in a form that was modified by the specialization pattern. E.g.
A specialization that is partial because the specialization is supplying at least one, but not all, of the template arguments.
template<typename T, typename U>
class G { public: T Foo(T a, U b){ return a + b; }};
template<typename T>
class G<T, bool> { public: T Foo(T a, bool b){ return b ? ++a : a; }};
A specialization that is partial because the specialization is causing the supplied template argument to be used only partially.
template<typename T>
class F { public: T Foo(T a){ return ++a; }};
template<typename T>
class F<T*> { public: T Foo(T* a){ return ++*a; }};
In this second example if the template were instantiated using A<char*> then T within the template would actually be of type char, i.e. the template argument as supplied is used only partially due to the application of the specialization pattern.
If that is correct then wouldn't that make the template in the original question a full specialization rather than a partial specialization, and if that is not so then where is my misunderstanding?
It is a partial class template specialization for the case when the same type is passed for both parameters.
Maybe this will be easier to read:
template<typename T, typename U>
struct is_same : std::false_type {};
template<typename T>
struct is_same<T,T> : std::true_type {};
EDIT:
When in doubt whether a specialization is an explicit (full) specialization or a partial specialization, you can refer to the standard which is pretty clear on this matter:
n3337, 14.7.3./1
An explicit specialization of any of the following:
[...]
can be declared by a declaration introduced by template<>; that is:
explicit-specialization:
template < > declaration
and n3337, 14.5.5/1
A primary class template declaration is one in which the class
template name is an identifier. A template declaration in which the
class template name is a simple-template-id is a partial
specialization of the class template named in the simple-template-id. [...]
Where simple-template-id is defined in the grammar like this:
simple-template-id:
template-name < template-argument-list opt >
template-name
identifier
So, wherever there's template<>, it's a full specialization, anything else is a partial specialization.
You can also think about it this way: Full template specialization specializes for exactly one possible instantiation of the primary template. Anything else is a partial specialization. Example in your question is a partial specialization because while it limits the arguments to be of the same type, it still allows for indifinitely many distinct arguments the template can be instantiated with.
A specialization like this, for example
template<>
vector<bool> { /* ... */ };
is a full specialization because it kicks in when the type is bool and only bool.
Hope that helps.
And just a note I feel it's worth mentioning. I guess you already know - function templates can't be partialy specialized. While this
template<typename T>
void foo(T);
template<typename T>
void foo(T*);
might looks like a partial specialization of foo for pointers on the first glance, it is not - it's an overload.
You mention specifying "full or partial types" when performing specialization of a template, which suggests that you are aware of such language feature as partial specialization of class templates.
Partial specialization has rather extensive functionality. It is not limited to simply specifying concrete arguments for some of the template parameters. It also allows defining a dedicated version of template for a certain groups of argument types, based on their cv-qualifications or levels of indirection, as in the following example
template <typename A, typename B> struct S {};
// Main template
template <typename A, typename B> struct S<A *, B *> {};
// Specialization for two pointer types
template <typename A, typename B> struct S<const A, volatile B> {};
// Specialization for const-qualified type `A` and volatile-qualified type `B`
And it also covers specializations based on whether some template arguments are identical or different
template <typename A> struct S<A, A> {};
// Specialization for two identical arguments
template <typename A> struct S<A, A *> {};
// Specialization for when the second type is a pointer to the first one
As another, rather curios example, partial specialization of a multi-argument template can be used to fully override the main template
template <typename A, typename B> struct S<B, A> {};
// Specialization for all arguments
Now, returning to your code sample, partial specialization for two identical arguments is exactly what is used in the code you posted.