Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

g++ and clang++ different behaviour with template specialization for auto argument

Playing with C++17 auto template arguments I've encountered another g++/clang++ disagreement.

Given the following simple code

template <auto>
struct foo;

template <int I>
struct foo<I>
 { };

int main ()
 {
   foo<42l> f42; // <--- long constant, not int constant

   (void)f42; // avoid the "unused variable" warning
 }

I see that clang++ (8.0.0, by example) compile the code where g++ (9.2.0, by example) gives the following error

prog.cc: In function 'int main()':
prog.cc:12:13: error: aggregate 'foo<42> f42' has incomplete type and cannot be defined
   12 |    foo<42l> f42;
      |             ^~~

Both compilers compile if we use a int constant instead of a long constant

  foo<42>  f42;  // compile with both clang++ and g++

So I have two questions for C++ language layers

(1) it's legal, in C++17, specialize a template, declared receiving an auto template parameter, for a value of a specific type (as the foo specialization in my code)?

(2) if the answer to the preceding question is "yes", a template specialization can intercept a value of a different (but convertible) type?

Question (2) is almost: is right clang++ or g++?

like image 281
max66 Avatar asked Aug 21 '19 13:08

max66


1 Answers

Here's a slightly different repro that doesn't rely on incomplete types:

template <auto> struct foo { static constexpr int value = 0; };
template <int I> struct foo<I> { static constexpr int value = 1; };

// ok on gcc, fires on clang which thinks foo<42L>::value is 1
static_assert(foo<42L>::value == 0);

This is a clang bug. 42L clearly matches auto, no question there. But does it match int I? No, from [temp.deduct.type]/19:

If P has a form that contains <i>, and if the type of i differs from the type of the corresponding template parameter of the template named by the enclosing simple-template-id, deduction fails. If P has a form that contains [i], and if the type of i is not an integral type, deduction fails. [ 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
}


template<const short cs> class B { };
template<short s> void g(B<s>);
void k2() {
  B<1> b;
  g(b);             // OK: cv-qualifiers are ignored on template parameter types
}

end example ]

In order to see if 42L matches the specialization, we need to deduce int I from 42L and that fails. Hence, we stick with the primary specialization. clang does not do this. Filed 43076.

like image 152
Barry Avatar answered Nov 12 '22 23:11

Barry