There is disagreement between gcc/clang and msvc when trying to compile the following code:
struct foo {
};
// primary template
template<auto>
struct nttp {
static constexpr int specializaion = 0;
};
// specialization
template<foo f>
struct nttp<f> {
static constexpr int specializaion = 1;
};
int main() {
// Does not compile with msvc 19.30
nttp<5> x{};
}
Full example here.
Msvc is complaining that there is no viable conversion from int to foo which is obviously true, but shouldn't be of any relevance here as far as I understand the rules of partial template specialization.
Quoting cppreference:
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
If more than one specialization matches, partial order rules are used to determine which specialization is more specialized. The most specialized specialization is used, if it is unique (if it is not unique, the program cannot be compiled)
If no specializations match, the primary template is used
I'd argue that there is no matching spezialization for int in this case hence the primary template should be selected.
Interestingly, the situation can be fixed by constraining the auto in the specialization by a concept:
template<class T>
concept Foo = std::is_same_v<T, foo>;
template<auto>
struct nttp {
static constexpr int specializaion = 0;
};
template<Foo auto f>
struct nttp<f> {
static constexpr int specializaion = 2;
};
// compiles with all compilers
static_assert(nttp<5>{}.specializaion == 0);
Function templates work as expected, too:
constexpr int test(auto) {
return 0;
}
constexpr int test(foo) {
return 1;
}
// compiles with all compilers
static_assert(test(5) == 0);
Long story short: empirically and intuitively I'd say that this is a bug in MSVC, but as always there's a chance that UB is involved here. So the question would be: are clang/gcc correct, or MSVC, or even all of them?
This is open MSVC bug report:
Specialization of class template with auto parameter fails to compile for an unclear reason
Your program is well-formed as per [temp.class.spec.match]/2 and [temp.class.spec.match]/3:
/2 A partial specialization matches a given actual template argument
list if the template arguments of the partial specialization can be
deduced from the actual template argument list, and the deduced
template arguments satisfy the associated constraints of the partial
specialization, if any.
/3 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.
/3 was specifically updated as part of P0127R2 (Declaring non-type template parameters with auto), which re-wrote the previously revised wording from the resolution of CWG1315
(After CWG1315, before P0123R2) /3 Each template-parameter shall appear at least once in the template-id outside a non-deduced context.
This re-write was done particularly to allow partial specialization over non-template parameters declared with the auto placeholder type.
Related
In the next program, struct template A<int> has a specialization A<char>:
template <int>
struct A { constexpr operator int() { return 1; } };
template <char c>
struct A<c> { constexpr operator int() { return 2; } };
int main() {
static_assert( A<1000>{} == 1 ); //ok in Clang and GCC
static_assert( A<1>{} == 2 ); //ok in Clang only
}
Clang accepts the whole program.
GCC accepts the specialization definition, but ignores it in A<1>{}.
MSVC complains on such specialization:
error C2753: 'A<c>': partial specialization cannot match argument list for primary template
Demo: https://gcc.godbolt.org/z/Ef95jv5E5
Which compiler is right here?
The active CWG issue 1647 mentions exactly this case of specializing an int non-type template parameter to a char.
It also mentions that the standard is currently lacking wording to handle type mismatches between non-type template parameters in primary templates and their partial specializations and that there is implementation divergence on the issue.
I have a template with two parameters: the first is a type, and the second is a function pointer with an argument whose type is the first template parameter. This MCVE works:
void returnsVoid(int x) { }
template <typename T, void (*Func)(T)>
struct foo { void bar(T t) { Func(t); } };
int main(int, char *[]) {
foo<int, returnsVoid> a; // ok
}
However, when I change the return type of the second template parameter to auto (as explained in this related question), I get an error:
void returnsVoid(int x) { }
template <typename T, auto (*Func)(T)>
struct foo {
void bar(T t) { Func(t); }
};
int main(int, char *[]) {
foo<int, returnsVoid> a; // error: unable to deduce ‘auto (*)(T)’ from ‘returnsVoid’
// note: mismatched types ‘T’ and ‘int’
}
Why does this no longer work with an auto return type?
I'm using g++ 9.3.0 on an Ubuntu Linux machine. A similar error occurs in clang 10.
This is gcc's bug. (Bug 83417)
It seems gcc failing deducing the type of auto when using the 1st template parameter T as function parameter; you can use std::type_identity (since C++20) to exclude it from participating in template argument deduction.
// workaround for gcc
template <typename T, auto (*Func)(std::type_identity_t<T>)>
struct foo {
void bar(T t) { Func(t); }
};
LIVE
BTW: Clang 10 seems working well.
All standard references below refer, unless noted otherwise, to N4861 (March 2020 post-Prague working draft/C++20 DIS).
TL;DR;
Jump to the A workaround to mitigate the GCC bug section in the bottom of this post for a workaround, accepted by GCC and Clanng, which still relies of deduction of dependent types to avoid a client having to specify a second template argument for the actual type of the function parameter associated with the "main" template argument; namely the function pointer used associated non-type template parameter.
Standardese
As per [temp.deduct.type]/13:
When the value of the argument corresponding to a non-type template
parameter P that is declared with a dependent type is deduced from
an expression, the template parameters in the type of P are deduced
from the type of the value. [ Example:
template<long n> struct A { };
template<typename T> struct C;
template<typename T, T n> struct C<A<n>> {
using Q = T;
};
using R = long;
using R = C<A<2>>::Q; // OK; T was deduced as long from the
// template argument value in the type A<2>
— end example ]
any dependent types in the declaration of a non-type template parameter that undergoes type deduction (from an expression) shall also be deduced from the associated argument for the non-type template parameter. It is also essential that [temp.deduct.type]/5, covering the non-deduced contexts, does not apply for general uses of dependent types within non-type template parameters that are function pointers; meaning in the OP's example, T is a dependent type and is thus deduced from the value of the argument to the non-type (function pointer) template parameter.
A common problem when a given, say, type template parameter is deduced from more than one source (e.g. as in the example of OP), is that deduction yields different types; e.g. as showing in the following blog post:
template<typename T>
struct Foo { T t; };
template<typename T>
void addToFoo(Foo<T>& foo, T val) { foo.t += val; }
int main() {
Foo<long> f{42};
addToFoo(f, 13); // error: no matching function for call to 'addToFoo'
// note: candidate template ignored: deduced conflicting
// types for parameter T (long vs. int).
return 0;
}
As has been shown in #songyuanyao: answer (and as is shown also in the blog post), a type identity transformation trait can be used to intentionally place a given template parameter in a non-deduced context for cases where several deduction sources yields conflicting results.
However, the root cause of OP:s failure is not conflicting deduction results (this is a red herring), but rather GCC:s failure to correctly deduce template parameter from when another template parameter is deduced, where the former is present as a dependent type.
Thus, if we go back to [temp.deduct.type]/13, for the following class template and subsequent partial specialization:
// #1
template <auto>
struct A { static void dispatch() = delete; };
// #2
template <typename T, void (*fun)(T)>
struct A<fun> {
static void dispatch() {
T t{};
fun(t);
}
};
the following:
A<f>::dispatch(); // #3
is well-formed if f is a function with a single argument (of a type that is default-constructible) which returns void, e.g.
void f(int) { std::cout << "void f(int)\n"; }
// -> #3 is well-formed
as this will match the partial specialization at #2, deducing T to int and the non-type template parameter (which decides the specialization blueprinted by the primary template), which is dependent on T in this partial specialization, to void(*)(int).
On the other hand, #3 is ill-formed if f does not return void, as the partial specialization at #2 is no longer viable.
void f(int) { std::cout << "int f(int)\n"; }
// -> #3 is ill-formed
The key here is that:
the partial specialization at #2 applies only for template arguments to A which match the second (non-type) template parameter of the specialization, and
the first template parameter of the specialization,T, is deduced from the deduction of the second (non-type) template parameter, as T is a dependent type in the declaration of the second template parameter.
Both GCC and Clang works as expected for the two cases above.
Now, if we consider the similar example as to that of #1 and #2 above:
// #4
template <auto>
struct B { static void dispatch() = delete; };
// #5
template <typename T, auto (*fun)(T)>
struct B<fun> {
static void dispatch() {
T t{};
fun(t);
}
};
// ... elsewere
// #6
B<f>::dispatch();
the same argument as above applies:
if template argument f refers to a function (now with less restrictions) that has a single argument (of a type that is default-constructible), then the partial specialization at #5 is viable, and its second non-type template parameter, which contains its first type template parameter as a dependent type, shall be used to deduce the latter.
The significant difference in this case is that the type of the non-type template parameter itself undergoes [temp.arg.nontype]/1:
If the type T of a template-parameter contains a placeholder type
([dcl.spec.auto]) or a placeholder for a deduced class type
([dcl.type.class.deduct]), the type of the parameter is the type
deduced for the variable x in the invented declaration
T x = template-argument ;
If a deduced parameter type is not permitted for a template-parameter
declaration ([temp.param]), the program is ill-formed.
but [temp.deduct.type]/13 still applies the same, and we may not that [temp.deduct.type]/13 was actually added as part of P0127R2 (Declaring non-type template parameters with auto) which introduced placeholder types for non-type template parameters for C++17.
Thus, the core issue is that GCC fails to perform dependent type deduction (as specified in [temp.deduct.type]/13) when the non-type template parameter in whose declaration the dependent type (to be deduced) is present is a function pointer (or, as shown in the linked to GCC bug report, as pointer to member) AND the non-type template parameter is declared with a placeholder type (auto).
#songyuanyao: answer shows a workaround to this bug, applied to OP's example, simply making the dependent type non-dependent, as the associated template parameter can be deduced from elsewhere (namely from the first template argument in OP's example). This would not work for the examples above, where we rely solely on the dependent type deduction in the deduction of the non-type template parameter to find the type of the type template parameter (which is the dependent type in the former).
For an actual client API, requiring the client to explicitly specify the type of the argument to the function which is provided as another argument, when the former is entirely deducible from the latter, is arguably redundant design, and opens up for client confusion when providing conflicting arguments for these two template parameters. Thus, we'd arguably like to fall back on the partial specialization technique shown above, but as shown in these answers, GCC fails us in this regard in case we'd like the client to not be restricted to a specific return type.
A workaround to mitigate the GCC bug
We can work our way around this, however, by using the same approach as above, still relying on [temp.deduct.type]/13, but by using an additional type template parameter (for the return type) in the partial specialization:
#include <iostream>
template <auto>
struct C { static void dispatch() = delete; };
template <typename T, typename Return, Return (*fun)(T)>
struct C<fun> {
static void dispatch() {
T t{};
fun(t);
}
};
void f(int) { std::cout << "void f(int)\n"; }
int g(int) { std::cout << "int f(int)\n"; return 0; }
int main() {
C<f>::dispatch();
C<g>::dispatch();
}
The client will not need to worry about the additional template parameters of the partial specialization, as they entirely deducible via the third non-type template parameter of the specialization, in whose declaration they are dependent. This final example is accepted by both GCC and Clang.
This is CWG2476: [dcl.spec.auto]/2 requires that auto used as (part of) a return type without a trailing-return-type appear only where the function declarator declares a function. The template parameter declaration doesn’t do that, so it’s invalid. One could argue that /5 allows it in a template parameter’s decl-specifier-seq regardless, but that doesn’t make sense because it would deprive the following of meaning:
template<auto g() -> int>
int f() {return g();}
That said, the deduction rules would work here, so this is a defect in the wording that will probably be fixed by saying that return type deduction just doesn’t happen in the cases where auto is allowed for other reasons.
As already stated by #songyuanyao this seems to be a gcc bug.
For <= c++17 One solution is to move type T form function signature to non-deduced context:
template <typename T>
struct identity { using type = T; };
and then
template <typename T, auto (*Func)(typename identity<T>::type)>
struct foo {
void bar(T t) { Func(t); }
};
Another approach is to change design and to move to a member function template. The auto type deduction works in this case:
void returnsVoid(int) { }
template <typename T>
struct foo {
template <auto (*Func)(T)>
void bar(T t) { Func(t); }
};
int main(int, char *[]) {
foo<int> a;
a.bar<returnsVoid>(3);
}
Live
Looks like the rules for template instantiation in Clang (3.8) and GNU C++ (4.9) are not the same. Here is an example:
#include <cstddef>
template <bool>
class Assert {
Assert(); // private constructor for Assert<false>
};
template <>
class Assert<true> { // implicit public constructor for Assert<true>
};
template <size_t N>
class A {
};
template <class T, size_t N>
T foo(A<N>) {
return T(N - 1);
}
template <class T>
T foo(A<0>) { // foo is not defined for N=0
Assert<false>();
return T(0);
}
int main(int argc, char **argv) {
foo<int>(A<3>());
return 0;
}
This minimal example shows a template function, foo, that is generalized over a type T and a natural number N. This function is not defined for N=0, so I'd like to use the Assert class to signal a compiler error if it is used this way.
This code is accepted by the GNU compiler (and by Visual C++ 2015, as well), but Clang gives an error for "calling a private constructor of class Assert<false>".
So who is right? As I see it, there is no call for foo<T,0>, so there is no need to instantiate this template...
EDIT: Accepting Clang's interpretation of the standard, what is a canonical way to enforce compile-time checks on template parameters?
I believe clang is correct, since Assert<false> is not a dependent type.
http://en.cppreference.com/w/cpp/language/dependent_name
Non-dependent names are looked up and bound at the point of template definition. This binding holds even if at the point of template instantiation there is a better match:
Don't make specializations that cannot be valid. Make them general purpose and use static_assert (with a dependent value) to check for invalid template argument types/values. static_assert(std::is_same<T, int>::value) or static_assert(N != 0)
Accepting Clang's interpretation of the standard, what is a canonical way to enforce compile-time checks on template parameters?
You can drop the "specialization"/overload of foo() for A<0> and define the general template as follows:
template <class T, size_t N>
T foo(A<N>) {
Assert<N != 0>();
return T(N - 1);
}
With C++11 you don't need to define your own static Assert and can use the language provided static_assert:
template <class T, size_t N>
T foo(A<N>) {
static_assert(N!=0, "N must be positive");
return T(N - 1);
}
Both compilers are correct. As usual, this is controlled by [temp.res]/8:
Knowing which names are type names allows the syntax of every template
to be checked. The program is ill-formed, no diagnostic required, if:
no valid specialization can be generated for a template or a substatement of a constexpr if statement ([stmt.if]) within a
template and the template is not instantiated, or
every valid specialization of a variadic template requires an empty template parameter pack, or
a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend
on a template parameter, or
the interpretation of such a construct in the hypothetical instantiation is different from the interpretation of the
corresponding construct in any actual instantiation of the template.
Your template runs afoul of the third bullet point.
As to the correct solution, either a suitable static_assert can be used, or you can explicitly delete the undesirable overload:
template <class T>
T foo(A<0>) = delete;
The former allows better error messages, the latter plays more nicely with other metaprogramming.
I am trying to understand why a piece of template metaprogramming is not generating an infinite recursion. I tried to reduce the test case as much as possible, but there's still a bit of setup involved, so bear with me :)
The setup is the following. I have a generic function foo(T) which delegates the implementation to a generic functor called foo_impl via its call operator, like this:
template <typename T, typename = void>
struct foo_impl {};
template <typename T>
inline auto foo(T x) -> decltype(foo_impl<T>{}(x))
{
return foo_impl<T>{}(x);
}
foo() uses decltype trailing return type for SFINAE purposes. The default implementation of foo_impl does not define any call operator. Next, I have a type-trait that detects whether foo() can be called with an argument of type T:
template <typename T>
struct has_foo
{
struct yes {};
struct no {};
template <typename T1>
static auto test(T1 x) -> decltype(foo(x),void(),yes{});
static no test(...);
static const bool value = std::is_same<yes,decltype(test(std::declval<T>()))>::value;
};
This is just the classic implementation of a type trait via expression SFINAE:
has_foo<T>::value will be true if a valid foo_impl specialisation exists for T, false otherwise. Finally, I have two specialisations of the the implementation functor for integral types and for floating-point types:
template <typename T>
struct foo_impl<T,typename std::enable_if<std::is_integral<T>::value>::type>
{
void operator()(T) {}
};
template <typename T>
struct foo_impl<T,typename std::enable_if<has_foo<unsigned>::value && std::is_floating_point<T>::value>::type>
{
void operator()(T) {}
};
In the last foo_impl specialisation, the one for floating-point types, I have added the extra condition that foo() must be available for the type unsigned (has_foo<unsigned>::value).
What I don't understand is why the compilers (GCC & clang both) accept the following code:
int main()
{
foo(1.23);
}
In my understanding, when foo(1.23) is called the following should happen:
the specialisation of foo_impl for integral types is discarded because 1.23 is not integral, so only the second specialisation of foo_impl is considered;
the enabling condition for the second specialisation of foo_impl contains has_foo<unsigned>::value, that is, the compiler needs to check if foo() can be called on type unsigned;
in order to check if foo() can be called on type unsigned, the compiler needs again to select a specialisation of foo_impl among the two available;
at this point, in the enabling condition for the second specialisation of foo_impl the compiler encounters again the condition has_foo<unsigned>::value.
GOTO 3.
However, it seems like the code is happily accepted both by GCC 5.4 and Clang 3.8. See here: http://ideone.com/XClvYT
I would like to understand what is going on here. Am I misunderstanding something and the recursion is blocked by some other effect? Or maybe am I triggering some sort of undefined/implementation defined behaviour?
has_foo<unsigned>::value is a non-dependent expression, so it immediately triggers instantiation of has_foo<unsigned> (even if the corresponding specialization is never used).
The relevant rules are [temp.point]/1:
For a function template specialization, a member function template specialization, or a specialization for a member function or static data member of a class template, if the specialization is implicitly instantiated because it is referenced from within another template specialization and the context from which it is referenced depends on a template parameter, the point of instantiation of the specialization is the point of instantiation of the enclosing specialization. Otherwise, the point of instantiation for such a specialization immediately follows the namespace scope declaration or definition that refers to the specialization.
(note that we're in the non-dependent case here), and [temp.res]/8:
The program is
ill-formed, no diagnostic required, if:
- [...]
- a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or
- the interpretation of such a construct in the hypothetical instantiation is different from the interpretation of the corresponding construct in any actual instantiation of the template.
These rules are intended to give the implementation freedom to instantiate has_foo<unsigned> at the point where it appears in the above example, and to give it the same semantics as if it had been instantiated there. (Note that the rules here are actually subtly wrong: the point of instantiation for an entity referenced by the declaration of another entity actually must immediately precede that entity rather than immediately following it. This has been reported as a core issue, but it's not on the issues list yet as the list hasn't been updated for a while.)
As a consequence, the point of instantiation of has_foo within the floating-point partial specialization occurs before the point of declaration of that specialization, which is after the > of the partial specialization per [basic.scope.pdecl]/3:
The point of declaration for a class or class template first declared by a class-specifier is immediately after the identifier or simple-template-id (if any) in its class-head (Clause 9).
Therefore, when the call to foo from has_foo<unsigned> looks up the partial specializatios of foo_impl, it does not find the floating-point specialization at all.
A couple of other notes about your example:
1) Use of cast-to-void in comma operator:
static auto test(T1 x) -> decltype(foo(x),void(),yes{});
This is a bad pattern. operator, lookup is still performed for a comma operator where one of its operands is of class or enumeration type (even though it can never succeed). This can result in ADL being performed [implementations are permitted but not required to skip this], which triggers the instantiation of all associated classes of the return type of foo (in particular, if foo returns unique_ptr<X<T>>, this can trigger the instantiation of X<T> and may render the program ill-formed if that instantiation doesn't work from this translation unit). You should prefer to cast all operands of a comma operator of user-defined type to void:
static auto test(T1 x) -> decltype(void(foo(x)),yes{});
2) SFINAE idiom:
template <typename T1>
static auto test(T1 x) -> decltype(void(foo(x)),yes{});
static no test(...);
static const bool value = std::is_same<yes,decltype(test(std::declval<T>()))>::value;
This is not a correct SFINAE pattern in the general case. There are a few problems here:
if T is a type that cannot be passed as an argument, such as void, you trigger a hard error instead of value evaluating to false as intended
if T is a type to which a reference cannot be formed, you again trigger a hard error
you check whether foo can be applied to an lvalue of type remove_reference<T> even if T is an rvalue reference
A better solution is to put the entire check into the yes version of test instead of splitting the declval portion into value:
template <typename T1>
static auto test(int) -> decltype(void(foo(std::declval<T1>())),yes{});
template <typename>
static no test(...);
static const bool value = std::is_same<yes,decltype(test<T>(0))>::value;
This approach also more naturally extends to a ranked set of options:
// elsewhere
template<int N> struct rank : rank<N-1> {};
template<> struct rank<0> {};
template <typename T1>
static no test(rank<2>, std::enable_if_t<std::is_same<T1, double>::value>* = nullptr);
template <typename T1>
static yes test(rank<1>, decltype(foo(std::declval<T1>()))* = nullptr);
template <typename T1>
static no test(rank<0>);
static const bool value = std::is_same<yes,decltype(test<T>(rank<2>()))>::value;
Finally, your type trait will evaluate faster and use less memory at compile time if you move the above declarations of test outside the definition of has_foo (perhaps into some helper class or namespace); that way, they do not need to be redundantly instantiated once for each use of has_foo.
It's not actually UB. But it really shows you how TMP is complex...
The reason this doesn't infinitely recurse is because of completeness.
template <typename T>
struct foo_impl<T,typename std::enable_if<std::is_integral<T>::value>::type>
{
void operator()(T) {}
};
// has_foo here
template <typename T>
struct foo_impl<T,typename std::enable_if<has_foo<unsigned>::value && std::is_floating_point<T>::value>::type>
{
void operator()(T) {}
};
When you call foo(3.14);, you instantiate has_foo<float>. That in turn SFINAEs on foo_impl.
The first one is enabled if is_integral. Obviously, this fails.
The second foo_impl<float> is now considered. Trying to instantiate it, the compiles sees has_foo<unsigned>::value.
Back to instantiating foo_impl: foo_impl<unsigned>!
The first foo_impl<unsigned> is a match.
The second one is considered. The enable_if contains has_foo<unsigned> - the one the compiler is already trying to instantiate.
Since it's currently being instantiated, it's incomplete, and this specialization is not considered.
Recursion stops, has_foo<unsigned>::value is true, and your code snippet works!
So, you want to know how it comes down to it in the standard? Okay.
[14.7.1/1] If a class template has been declared, but not defined, at the point of instantiation ([temp.point]), the instantiation yields an incomplete class type.
(incomplete)
I have the following C++11 code.
#include <type_traits>
using IntType = unsigned long long;
template <IntType N> struct Int {};
template <class T>
struct is_int : std::false_type {};
template <long long N>
struct is_int<Int<N>> : std::true_type {};
int main()
{
static_assert (is_int<Int<0>>::value, "");
return 0;
}
Clang++ 3.3 compiles the code but on g++ 4.8.2 static assertion fails
$ g++ -std=c++11 main.cpp
main.cpp: In function ‘int main()’:
main.cpp:15:5: error: static assertion failed:
static_assert (is_int<Int<0>>::value, "");
^
$
The problem is caused by different integral template parameters.
Which compiler is right in this case?
The surprise
This is a subtle Clang bug, deeply buried in the Standard. The problem is that in almost all cases, non-type template arguments can be converted to the type of the template parameter. E.g. the expression Int<0> has an int literal argument of value 0 that is being converted to the type unsigned long long of the template parameter N.
14.8.2 Template argument deduction [temp.deduct]/2 2nd bullet
-- Non-type arguments must match the types of the corresponding non-type
template parameters, or must be convertible to the types of the
corresponding non-type parameters as specified in 14.3.2, otherwise
type deduction fails.
Since your class template is_int<T> has a partial specialization, we need to look at
14.5.5.1 Matching of class template partial specializations [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.
2 A partial specialization matches a given actual template argument
list if the template arguments of the partial specialization can be
deduced from the actual template argument list (14.8.2).
So it would seem that we can proceed to the earlier quote of 14.8.2/2 2nd bullet and match the second specialization (although in that case an even more complicated overload resolution game would have to be played).
The resolution
However, it turns out (as mentioned by #DyP in the comments) that another clause in the Standard supersedes this:
14.8.2.5 Deducing template arguments from a type [temp.deduct.type]
17 If, in the declaration of a function template with a non-type
template-parameter, the non-type templateparameter is used in an
expression in the function parameter-list and, if the corresponding
template-argument is deduced, the template-argument type shall match
the type of the template-parameter exactly, except that a
template-argument deduced from an array bound may be of any integral
type. [ Example:
template<int i> class A { / ... / };
template<short s> void f(A<s>);
void k1() {
A<1> a;
f(a); // error: deduction fails for conversion from int to short
f<1>(a); // OK
}
The upshot is that the partial specialization of is_int cannot be deduced because it does not take the exact same type (unsigned long long vs long long) as the Int class template's formal non-type template parameter.
You can resolve this by giving the non-type template parameter N in the partial specialization of is_int the same type as the non-type parameter N in the primary template Int.
template <IntType N>
// ^^^^^^^^
struct is_int<Int<N>> : std::true_type {};
Live Example.
Clang is being inconsistent. Since it accepts your code, I'm expecting the following code must output f(Int<long long>) instead of f(T):
using IntType = unsigned long long;
template <IntType N> struct Int {};
template<typename T>
void f(T) { std::cout << "f(T)" << std::endl; }
template<long long N>
void f(Int<N>) { std::cout << "f(Int<long long>)" << std::endl; }
int main()
{
f(Int<0>{});
}
But surprisingly, it outputs this (online demo):
f(T)
That shows Int<0> does NOT match with the second overload which accepts the argument as Int<N>. If that is so, then why does it match with Int<N> when it is used as template argument to the class template (in your case)?
My conclusion:
If Clang is correct in my case, then it is incorrect in your case.
If Clang is correct in your case, then it is incorrrect in my case.
Either way, Clang seems to have bug.
GCC, on the other hand, is consistent at least. That doesn't prove though that it doesn't have bug — it might mean that it has bug in both cases! Unless someone comes up with the standardese and showing it has bug too, I'm going to trust GCC in this case.