Compiler errors on partial template speciailzation (c++) - c++

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.

Related

Template specialisation with default argument [duplicate]

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.

Can't use base class template member function

For some reason, I can see the top most template<typename T> X<...>::fn(T&&), but not the base class versions. Importing them using the using keyword doesn't work. As I understand it:
In Class definition
... If the derived class already has a member with the same name, parameter list, and qualifications, the derived class member hides or overrides (doesn't conflict with) the member that is introduced from the base class.
Sample code:
#include <iostream>
#include <type_traits>
#define ENABLE_IF(...) std::enable_if_t<__VA_ARGS__, int> = 0
struct dummy {};
template<typename T>
struct always_false : std::false_type {};
template<typename...Ts>
struct X;
template<typename Tx, typename T, typename...Ts>
struct X<Tx, T, Ts...> : X<Tx, Ts...>
{
using X<Tx, Ts...>::fn;
template<typename R, ENABLE_IF(std::is_same<T, R>::value)>
auto fn(R&& x)
{
return x;
}
};
template<typename Tx>
struct X<Tx>
{
template<typename R>
auto fn(R&& x)
{
static_assert(always_false<R>::value, "Invalid type");
}
};
int main()
{
X<dummy, int, float> x;
std::cout << x.fn(1) << std::endl;
std::cout << x.fn(1.f) << std::endl;
std::cout << "Hello, world!\n";
}
I've tried this on g++, clang and VC++ and they all have various errors, (ambiguous call, member function disabled and could not deduce). It's interesting to note that g++ is failing on the call to X<dummy, int, float>::fn(int&&), whereas clang and VC++ are failing on calling X<dummy, int, float>::fn(float&&).
As I understand it, the compiler should ignore the absolute base class member function template<typename R> R X<dummy>::fn(R&&) when calling X<dummy, int, float>::fn(float&&), because that template should resolve to float X<dummy>::fn(float&&) which is an exact match to derived member function float X<dummy, float>::fn(float&&) requiring the derived one to be called without ambiguity.
What am I doing wrong? What am I not understanding?
Edit
To paraphrase T.C.'s answer so far, "this is what the spec says", which I would say is not the correct interpretation. The two points given there are in conflict with each other. If they are an equally good match (point 1), then only the most derived function signature should be visible (point 2).
Anyway, if the problem is a spec problem, then it should go away if I were to disable the possibility of a matching overload which would result in an ambiguity. Thus the following should work:
#include <iostream>
#include <type_traits>
#define ENABLE_IF(...) std::enable_if_t<__VA_ARGS__, int> = 0
template<typename T>
struct always_false : std::false_type {};
template<typename...Ts>
struct list {};
template<typename...Ts>
struct is_one_of;
template<template <typename...> class TT, typename T, typename T1, typename...Ts>
struct is_one_of<T, TT<T1, Ts...>> : is_one_of<T, TT<Ts...>> {};
template<template <typename...> class TT, typename T, typename...Ts>
struct is_one_of<T, TT<T, Ts...>> : std::true_type {};
template<template <typename...> class TT, typename T>
struct is_one_of<T, TT<>> : std::false_type {};
template<typename...Ts>
struct X;
template<typename L, typename T, typename...Ts>
struct X<L, T, Ts...> : X<L, Ts...>
{
using X<L, Ts...>::fn;
template<typename R, ENABLE_IF(std::is_same<T, R>::value)>
constexpr auto fn(R&& x) const
{
return x;
}
};
template<typename L>
struct X<L>
{
template<typename R, ENABLE_IF(!is_one_of<R, L>::value)>
constexpr auto fn(R&& x) const
{
static_assert(always_false<R>::value, "Type R didn't match");
}
};
template<typename...Ts>
struct XX : X<list<Ts...>, Ts...> {};
int main()
{
XX<int, float> x;
std::cout << x.fn(1) << std::endl;
std::cout << x.fn(2.f) << std::endl;
}
It does work under g++, but has the same problems under clang and VC++. So, is g++ the only one that is conforming here and the rest are defective?
Two things:
Your baseline catch-all version of fn is an equally good match as the other fn varieties; therefore at best you'll get an ambiguous overload error.
Hiding for using-declarations does not consider the full signature (which for function templates would include the template parameter list). It only considers 1) name, 2) (function) parameter-type-list, 3) cv-qualification, and 4) ref-qualifier (if any). If all four match, the base class function template is hidden and not brought in by the using-declaration. Notably, the template parameter list is not considered. In your case, the only thing different between the various fns is the template parameter list; they all have the same name, same parameter-type-list, same cv-qualification, and same ref-qualifier (or lack thereof). Therefore, the most derived fn will hide all fns from the base classes.
GCC appears to not implement this part to spec and consider the template parameter list when deciding on hiding.
One possible fix for this part is to move the enable_if to a function parameter, which is considered by the hiding check.
The way overload resolution in C++ works is:
Name lookup to build a set of candidate functions and function templates.
For each candidate function template, perform template argument deduction. If deduction fails, remove it from the overload set. If deduction succeeds, replace the candidate function template with the deduced specialization.
Eliminate non-viable candidates from the set of candidates.
If the set is empty, error. Otherwise, find the best viable function in the candidates.
Hiding operates at the first step: if a declaration is hidden, it is not found by name lookup, and therefore it is not in the initial set of candidates and will not be considered by overload resolution under any circumstance, regardless of what happens in step 2, 3, or 4. A hidden declaration effectively doesn't exist for the purposes of overload resolution.
So, in your case, the base class fns are all hidden. What does it mean? It means that the only candidate found by name lookup is the int one from the most derived class, nothing else. If template argument deduction and substitution succeed, that function template will be called. If they fail (as in the x.fn(2.f) case), then there is no viable candidate left, and you get an error.

Ambiguous partial specialization depending on std::enable_if

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

Class template specialization priority/ambiguity

While trying to implement a few things relying on variadic templates, I stumbled accross something I cannot explain. I boiled down the problem to the following code snippet:
template <typename ... Args>
struct A {};
template <template <typename...> class Z, typename T>
struct test;
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I'm more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I'm variadic!" << std::endl;
}
};
int main() {
test<A, A<int>>::foo();
}
Under gcc, it produces an error because it considers both specializations to be equally specialized when trying to instantiate test<A, A<int>>:
main.cpp: In function 'int main()':
main.cpp:25:24: error: ambiguous template instantiation for 'struct test<A, A<int> >'
test<A, A<int>>::foo();
^~
main.cpp:11:12: note: candidates are: template<template<class ...> class Z, class T> struct test<Z, Z<T> > [with Z = A; T = int]
struct test<Z, Z<T>> {
^~~~~~~~~~~~~
main.cpp:18:12: note: template<template<class ...> class Z, class T, class ... Args> struct test<Z, Z<T, Args ...> > [with Z = A; T = int; Args = {}]
struct test<Z, Z<T, Args...>> {
However, clang deems the first specialization "more specialized" (through partial ordering: see next section) as it compiles fine and prints:
I'm more specialized than the variadic spec, hehe!
A live demo can be found on Coliru. I also tried using gcc's HEAD version and got the same errors.
My question here is: since these two well-known compilers behave differently, which one is right and is this piece of code correct C++?
Standard interpretation (C++14 current draft)
From the sections §14.5.5.1 and $14.5.5.2 of the C++14 standard draft, partial ordering is triggered to determine which specialization should be chosen:
(1.2) — If more than one matching specialization is found, the partial order rules (14.5.5.2) are used to determine
whether one of the specializations is more specialized than the others. If none of the specializations
is more specialized than all of the other matching specializations, then the use of the class template is
ambiguous and the program is ill-formed.
Now according to §14.5.5.2, the class template specializations are transformed into function templates through this procedure:
For two class template partial specializations, the first is more specialized than the second if, given the
following rewrite to two function templates, the first function template is more specialized than the second
according to the ordering rules for function templates (14.5.6.2):
(1.1) — the first function template has the same template parameters as the first partial specialization and has
a single function parameter whose type is a class template specialization with the template arguments
of the first partial specialization, and
(1.2) — the second function template has the same template parameters as the second partial specialization
and has a single function parameter whose type is a class template specialization with the template
arguments of the second partial specialization.
Therefore, I tried to reproduce the issue with the function template overloads that the transformation described above should generate:
template <typename T>
void foo(T const&) {
std::cout << "Generic template\n";
}
template <template <typename ...> class Z, typename T>
void foo(Z<T> const&) {
std::cout << "Z<T>: most specialized overload for foo\n";
}
template <template <typename ...> class Z, typename T, typename ... Args>
void foo(Z<T, Args...> const&) {
std::cout << "Z<T, Args...>: variadic overload\n";
}
Now trying to use it like this:
template <typename ... Args>
struct A {};
int main() {
A<int> a;
foo(a);
}
yields a compilation error [ambiguous call] in both clang and gcc: live demo. I expected clang would at least have a behavior consistent with the class template case.
Then, this is my interpretation of the standard (which I seem to share with #Danh), so at this point we need a language-lawyer to confirm this.
Note: I browsed a bit LLVM's bug tracker and could not find a ticket for the behavior observed on function templates overloads in this question.
From temp.class.order:
For two class template partial specializations, the first is more specialized than the second if, given the following rewrite to two function templates, the first function template is more specialized than the second according to the ordering rules for function templates ([temp.func.order]):
Each of the two function templates has the same template parameters as the corresponding partial specialization.
Each function template has a single function parameter whose type is a class template specialization where the template arguments are the corresponding template parameters from the function template for each template argument in the template-argument-list of the simple-template-id of the partial specialization.
The order of:
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I'm more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I'm variadic!" << std::endl;
}
};
depends on the order of:
template <template <typename...> class Z, typename T>
void bar(test<Z, Z<T>>); // #1
template <template <typename...> class Z, typename T, typename ... Args>
void bar(test<Z, Z<T, Args...>>); // #2
From [temp.func.order]:
Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.
To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs ([temp.variadic]) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.
Using the transformed function template's function type, perform type deduction against the other template as described in [temp.deduct.partial].
By those paragraph, for any function transformed from any synthesized template Z0 and type T0, which can form #1, we can do type deduction with #2. But functions transformed from #2 with fictitious template Z2 with any type T2 and any non-empty set of Args2 can't be deduced from #1. #1 is obviously more specialized than #2.
clang++ is right in this case.
Actually, this one and this one are failed to compile (because of ambiguous) in both g++ and clang. It seems like both compilers have hard time with template template parameters. (The latter one is clearly ordered because its order is the same of no function call).

Template argument deduction for class templates and multiple parameters packs

In C++17 template arguments for a class template will be deduced more or less as it happens nowadays for a function template.
Here is the relevant paper.
As an example from the above mentioned paper:
template<class ... Ts> struct X { X(Ts...) };
X x1{1}; // OK X<int>
X x11; // OK X<>
Function templates have another interesting feature when deduction happens.
Consider the following code:
template<typename... U, typename... T>
auto func(T&&...) {}
// ...
// U is int, char - T is int, double
func<int, char>(0, .0);
We can have two parameters packs as long as deduction helps to discriminate between them.
No need to wrap them within a tuple or some other structure.
Will it be possible to do the same with class templates?
As an example:
template<typename... U, typename... T>
struct S {
S(T&&...) {}
};
// ...
// U is int, char - T is int, double
S<int, char> s{0, .0};
The paper contains the example below:
template<class ... Ts> struct X { X(Ts...) };
X<int> x3{1, 'a', "bc"}; // OK X<int,char,const char*>
Anyway, it isn't exactly the same thing and I'm not sure if it will be allowed or not.
This:
template<typename... U, typename... T>
struct S { ... };
is just ill-formed per [temp.param]:
If a template-parameter of a primary class template, primary variable template, or alias template is a template parameter pack, it shall be the last template-parameter.
This case:
template<class ... Ts> struct X { X(Ts...) };
X<int> x3{1, 'a', "bc"}; // OK X<int,char,const char*>
is problematic since X<int> is already a valid type. This part of the paper was dropped in Oulu, though it's possible some proposal in the future will make it possible to indicate that some of the class template parameters should be deduced but others should be specified:
X<string, ???> x3{"a", 1, "b"}; // X<string, int, const char*>.
where ??? is some series of tokens that makes intent clear.