Class template partial specialization: compiler error - c++

This program
#include <iostream>
template <int I>
struct A
{
A() { std::cout << "A<I>()\n"; }
};
template <int I>
struct A<I + 5>
{
A() { std::cout << "A<I + 5>()\n"; }
};
int main()
{
return 0;
}
is compiled neither by gcc HEAD 10.0.0 20190 nor by clang HEAD 10.0.0.
For example the gcc compiler issues error
prog.cc:10:8: error: template argument '(I + 5)' involves template parameter(s)
10 | struct A<I + 5>
| ^~~~~~~~
Is there a wrong class template partial specialization?

This is not a valid partial specialization (though the error could be better). The clause we are not inline with is the following:
[temp.class.spec]
8 Within the argument list of a class template partial
specialization, the following restrictions apply:
The specialization shall be more specialized than the primary template.
"The specialization is not more specialized!?" I suppose you think. But that is indeed the case. The rules for determining which is "more specialized" are described in [temp.class.order]. The gist of it is that we are to consider two hypothetical function template overloads:
template <int I>
struct A { /* ... */ };
template<int I>
void foo(A<I>); //1
template<int I>
void foo(A<I + 5>); //2
We then perform overload resolution and partial ordering of the function templates. If #2 wins, it's more specialized and your declaration is legal. If it doesn't win, the declaration is invalid. Partial ordering is done by cooking up some arguments and doing template argument deduction of one template against another. So suppose we start with comparing the first to the second (I'll rename them for simplicity, but they are still overloads):
void foo1(A<0>); -> void foo2(A<I + 5>);
Does the argument deduction succeed here? It doesn't. I + 5 is a non-deduced context:
[temp.deduct.type]
The non-deduced contexts are:
5.3 - A non-type template argument or an array bound in which a
subexpression references a template parameter.
The I references a template parameter, so I + 5 is a non-deduced context. Therefore template argument deduction fails in this direction.
Let's try the other direction. Again we cook up an argument
void foo2(A<1 + 5>); = void foo2(A<6>); -> void foo1(A<I>);
Deduction obviously succeeds here. So according to the rules of function template partial ordering, foo1 is more specialized than foo2. This means that our primary is really more specialized than our "partial specialization", making the partial specialization ill-formed.

Looking into temp.class.spec.match/3, we have
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.
with the example
template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {}; // error
template <int I> struct A<I, I> {}; // OK
template <int I, int J, int K> struct B {};
template <int I> struct B<I, I*2, 2> {}; // OK
Clang's error message echoes this:
error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
(See #StoryTeller's answer for why this deduction fails in your code, I won't duplicate that here.)

Related

Defaulted Template in Template Function Requires Empty Angle Brackets <>

gcc 11.2 can't seem to compile this:
template <typename T = int>
struct Test {};
template <typename T> void foo(T& bar) {}
int main()
{
Test t;
foo<Test>(t);
}
but has no problem with
template <typename T = int>
struct Test {};
template <typename T> void foo(T& bar) {}
int main()
{
Test t;
foo<Test<>>(t);
}
Is this a compiler bug?
This question seems to suggest it should work.
GCC is right. An empty template argument list, <> for a function template is allowed to be omitted ([temp.arg.explicit]/4). In other cases, the template argument list is generally required in order to name a particular specialization of a template, even if it is empty. See the grammar for simple-template-id, [temp.names]/1.
As a limited exception to the rule, if the name of a class template without a template argument list appears in a context where a concrete type is required, it is known as a "placeholder for a deduced class type" and this is only allowed in specific contexts listed in [dcl.type.class.deduct]. The most common one is a variable declaration like std::pair p("foo", 1) where the compiler will deduce std::pair<const char*, int> in C++17 and later.
In your code, you are trying to refer to a particular specialization of class template Test, without specifying the template argument list, and not in a context where the template arguments can be deduced. Therefore, it's not allowed.
The new Class template argument deduction (CTAD) (since C++17) is applied to declarations. The expression foo<Test<>>(t); is not a declaration, it is a template function call.
In your first code snippet, you specify a class template (Test) as a template parameter to foo and not a type (like an instance of a class template). You would need a function that takes a template template parameter to handle that.
Example:
#include <type_traits>
template<template<class> class T, class U> // T = Test, U = deduced to int
void foo(T<U>& bar) {
std::cout << std::is_same<T<U>, Test<int>>::value << '\n'; // true
}
int main() {
Test t;
foo<Test>(t); // now ok
}
In your second snippet, foo<Test<>>(t); instantiates foo<Test<int>>(Test<int>&); since <> makes the template instantiation use the default type for the template parameter, which is int.

C++ function template overload resolution with nested types

Why does overload resolution fail in this case ? I would have expected that foo<int> could be deduced.
template <typename T> struct Templ { typedef T Type; };
template <typename T> void foo(typename Templ<T>::Type) {}
foo(1); //error can't deduce template argument for T
The rules of C++ say that's not a deduced context. If one thinks about why that might be, there are a few things that comes to mind.
Deduction in this case is asking the compiler to invert a type-level function. That may be ambiguous or impossible. That requires introducing rules around specialization visibility. And such a function may be complex even when the solution is unambiguous:
template <std::uint32_t>
struct foo;
template <>
struct foo<0u> {
using type = std::integral_constant<int, 1>;
};
template <>
struct foo<1u> {
using type = std::integral_constant<int, 2>;
};
template <std::uint32_t N>
struct foo {
using type = std::integral_constant<int,
foo<N-1>::type::value + foo<N-2>::type::value
>;
};
template <std::uint32_t N>
void using_foo(typename foo<N>::type);
// would deduce N=20u
using_foo(std::integral_constant<int, 17711>{});
Additionally, it seems like deduction in such cases would introduce ODR hazards. By calling a function, with some arbitrary parameter, we would be deducing a parameter type through an unrelated type-function, and we require that the relevant specializations of that type-function be visible, which isn't something that's obvious at all at the call site.

How are template specializations chosen with default arguments and more

https://stackoverflow.com/a/22487113/4416169
in the following answer we can see the code in scrutiny:
#include <iostream>
#include <type_traits>
template <typename T, T N, typename = void >
struct X {
static const bool isZero = false;
};
template <typename T, T N>
struct X < T, N, typename std::enable_if<N == 0>::type > {
static const bool isZero = true;
};
int main(int argc, char* argv[]) {
std::cout << X <int, 0>::isZero << std::endl;
std::cout << X <int, 1>::isZero << std::endl;
return 0;
}
https://stackoverflow.com/a/35652391/4416169 here we can see how templates are chosen:
Choosing a template specialization happens in five steps:
Take the primary template declaration.
Fill in user-specified template arguments.
Function templates
only: Deduce additional template arguments.
Use defaults for
remaining template arguments.
Use the partial ordering
algorithm (C++14 14.5.6.2) to choose the best-matching
specialization.
firstly, im aware that SFINAE will take care of excluding the struct with the std::enable_if<N==0> as it will not have the ::type defined (making it compile to error, but of course substitution failure is NOT an ERROR, just exclude the template from the final specialization options, so beautiful) when N != 0. Since N!=0 always results in the formerly defined template being chosen(the unspecialized one), I wonder why is it that N=0 chooses the latterly defined template(the specialized one) over the former.
Thus following these steps we can further scrutinize(in the case of T=int and N=0):
template<typename T, T N, typename = void>
T is int, N is 0; template<int,0,(unnamed-type)>
do nothing we are not a function.
(unnamed-type) is void; template<int,0,void>
this is explained here: https://stackoverflow.com/a/17008568/4416169
We know now that when we try to access X<int,0>::isZero there are two options, the one with the default template type set to void by default, and the one where that same template type is set to void but not by default but explicitly.
Does the compiler always prefer the template(out of all available template specializations) with the least arguments?
Moreover and expanding: would this mean that the template with the least actual arguments is chosen over the one with the same amount of explicit(explicit as in non-defaulted or obligatory) arguments but that has the remainder set to a default value?
To be even more clear:
template<typename T, T N, typename C = void>
struct defTempArgStruct {
static constexpr unsigned int value = 0;
};
template<typename T, T N>
struct defTempArgStruct<T,N>
{
static constexpr unsigned int value = 99;
};
int main()
{
std::cout << defTempArgStruct<int, 2>::value << std::endl;
}
why does that code choose the second specialized template(<typename T, T N>),displaying 99, over the first? And why does it choose the first, displaying 0, when the second, specialized, one changes to be:
template<typename T, T N>
struct defTempArgStruct<T,N,float>
{
static constexpr unsigned int value = 99;
};
very confusing.
PLEASE be eager to correct anything anywhere that im getting wrong!
A viable partial specialization is always preferred over the primary template:
[temp.class.spec.match]/1 When a class template is used in a context that requires an instantiation of the class, it is necessary to determine whether the instantiation is to be generated using the primary template or one of the partial specializations. This is done by matching the template arguments of the class template specialization with the template argument lists of the partial specializations.
(1.1) — If exactly one matching specialization is found, the instantiation is generated from that specialization.
(1.2) — If more than one matching specialization is found, the partial order rules (17.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.
(1.3) — If no matches are found, the instantiation is generated from the primary template.

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).

Nested template specialization depending on enclosing template parameters

template < int ...Indices>
class T1 {
template <int _1, int _2>
class T2;
};
template <int ...Indices>
template <int _1>
class T1<Indices...>::T2<_1, sizeof...(Indices)> {};
//^--error: non-type template argument depends on a template parameter of the partial specialization
compiles on gcc 4.5+ but neither on clang 3.1 nor icc, both complaining about the usage of sizeof...(Indices).
Is it just a not-yet implemented feature in the latter compilers or some special case?
Thanks,
Buote
The standard says in [temp.class.spec] paragraph 8 that
Within the argument list of a class template partial specialization, the following restrictions apply:
— A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier. [ Example:
template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {}; // error
template <int I, int J> struct B {};
template <int I> struct B<I, I> {}; // OK
— end example ]
The purpose of the rule is to disallow partial specializations based on non-trivial expressions like those in the example, and sizeof...(Indices) is not a simple identifier so maybe clang and ICC are right to reject it. I'm not sure which compiler is right, to be honest. I suggest reporting a bug to one of the compilers and if they say their implementation is correct report it to the others for interpreting it differently!
You could attempt to try:
template < int ...Indices>
class T1
{
static const int index_size = sizeof...(Indices);
template <int _1, int _2>
class T2;
};
template <int ...Indices>
template <int _1>
class T1<Indices...>::T2<_1, T1<Indices...>::index_size> {};
and see if the compiler issue is with the actual sizeof... operator, or if it's with the way it's being used in the template declaration.