Search code examples
c++language-lawyerc++17variadic-templatestemplate-argument-deduction

g++ and clang++ different behaviour deducing variadic template `auto` values


Another "who's right between g++ and clang++?"

This time I'm convinced it's a g++ bug, but I ask for a confirm from standard gurus.

Given the following code

template <template <auto...> class Cnt,
          typename ... Types,
          Types ... Vals>
void foo (Cnt<Vals...>)
 { }

template <auto ...>
struct bar
 { };

int main ()
 {
   foo(bar<0, 1>{});  // compile both
   foo(bar<0, 1L>{}); // only clang++ compile; error from g++
 }

Live demo

clang++ (8.0.0, by example) compile and link without problem where g++ (9.2.0, by example) gives the following error compiling the second foo() (but not the first) call

prog.cc: In function 'int main()':
prog.cc:16:20: error: no matching function for call to 'foo(bar<0, 1>)'
   16 |    foo(bar<0, 1L>{}); // only clang++ compile; error from g++
      |                    ^
prog.cc:6:6: note: candidate: 'template<template<auto ...<anonymous> > class Cnt, class ... Types, Types ...Vals> void foo(Cnt<Vals ...>)'
    6 | void foo (Cnt<Vals...>)
      |      ^~~
prog.cc:6:6: note:   template argument deduction/substitution failed:
prog.cc:16:20: note:   mismatched types 'int' and 'long int'
   16 |    foo(bar<0, 1L>{}); // only clang++ compile; error from g++
      |                    ^
prog.cc:16:20: note:   'bar<0, 1>' is not derived from 'Cnt<Vals ...>'

If I understand correctly, g++ require that the Types... of the Vals... coincide where clang++ accept that the Vals... are of different Types....

Who's right?

-- EDIT --

As pointed by Marek R (thanks) also MSVC (v19.22) fail to compile.

But, if I understand correctly, fail also compiling the first foo() call with the following error

<source>(13): error C2672: 'foo': no matching overloaded function found
<source>(13): error C2893: Failed to specialize function template 'void foo(Cnt<Vals...>)'
<source>(13): note: With the following template arguments:
<source>(13): note: 'Cnt=bar'
<source>(13): note: 'Types={}'
<source>(13): note: 'Vals={0, 1}'

-- EDIT 2 --

camp0 observes (thanks) that g++ compile this code until version 7.4.

A bug introduced from 8.1 or is bugged my code and g++ has corrected his code from 8.1?


Solution

  • None of the three compilers is correct.

    From [temp.param]/17:

    If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack ([dcl.fct]), then the template-parameter is a template parameter pack. A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded packs is a pack expansion. ... A template parameter pack that is a pack expansion shall not expand a template parameter pack declared in the same template-parameter-list. [ Example:

    ...

    template <class... T, T... Values>              // error: Values expands template type parameter
      struct static_array;                          // pack T within the same template parameter list
    

    — end example ]

    So the code is ill-formed, even without the line foo(bar<0, 1L>{});.

    There is already a Clang bug report and a GCC bug report.