Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Clang prefer the primary template over the specialization from C++17?

The following program is reduced from the code in this question:

template <typename T, void (*)(T), typename = void>  
struct S;

template <typename T, void (*f)(T)>
struct S<T, f, void> {};

S<int const, nullptr> s;

In all versions of GCC, in all language revisions, the specialization of S is chosen when s is instantiated.

In all versions of Clang, but only from C++17 onward, the primary template is chosen when instantiating s.

Some points I think worth noting are that the primary is never chosen if the instantiation is over <int, nullptr>, i.e. the first parameter is no longer int const. Also, the primary is never chosen if the signature of the function pointer in the second parameter doesn't contain T as the argument, i.e. if the second parameter is T (*)(), or void (*)(), say.

If this code isn't IFNDR, which compiler is correct? Is there some breaking change in the C++17 language revision?

like image 932
cigien Avatar asked Mar 22 '21 05:03

cigien


1 Answers

This is because C++17 allowed a template type argument to be deduced from the type of a non-type argument. [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.

So when we try to match S<int const, nullptr> against the partial specialization, we deduce the partial specialization's template parameter T from two sources:

  • From the first template argument int const, we deduce T = int const
  • From the second template argument (which has type void (*)(int) because the top-level cv-qualification of function parameters are adjusted away), we deduce T = int.

Since we deduced conflicting results, the deduction fails and the partial specialization is not a match.

Similar examples were brought up on the core reflector back in 2019. There was some agreement that this was a defect in the standard, and that deduction from the type of a non-type template argument should only happen for things that are not otherwise deducible.

like image 99
T.C. Avatar answered Nov 04 '22 17:11

T.C.