g++ 和 clang++ 对于推断可变参数模板中的 `auto` 值的行为有所不同

17

又是一次关于"g++和clang++谁更正确"的讨论。

这一次我相信是g++的一个bug,但我想从标准大师那里确认一下。

下面是给定的代码:

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++
 }

实时演示

使用clang++(例如8.0.0)编译和链接没有问题,而使用g ++(例如9.2.0)编译第二个foo()(但不是第一个)调用时会出现以下错误:

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

如果我理解正确,g++要求Vals...Types...相符,而clang++允许Vals...具有不同的Types...

谁是对的?

--编辑--

如Marek R所指出的(感谢),MSVC(v19.22)也无法编译。

但是,如果我理解正确,第一个foo()调用也会失败,出现以下错误。

<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}'

-- 编辑2 --

camp0指出(感谢)g++编译此代码直到7.4版本。

是否从8.1引入了错误或者我的代码有问题,而g++已经从8.1纠正了他的代码?


3
msvc 报告了类似的问题(我提供了 godbolt 演示)。你需要寻求法律意见。 - Marek R
@MarekR - 哎呀!你说得对:需要“语言律师”。谢谢。无论如何,看到你的演示,我发现msvc在第一次调用时也失败了。 - max66
1
看起来使用gcc 7.4可以运行该代码,不确定他们是否故意在8.1中引入了问题或者这是一个bug。 - camp0
1个回答

11

三个编译器都不正确。

来自[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 ]

因此,即使没有foo(bar<0, 1L>{});这行代码,代码仍然不规范。

已经有Clang 错误报告和GCC 错误报告


有趣。我完全不知道在同一参数列表中不能声明和扩展模板包。如果我理解正确,你引用的是其他C++标准化版本。但是我已经通过最新的C++17草案进行了验证,并且相同的短语(以及相同的示例)都存在。只有一个异议:既然重点在于在同一参数列表中无法声明和扩展模板包,g++是否也存在漏洞?请注意,g ++编译foo(bar<0、1> {});。如果错误在于foo()的声明,那么编译器如何正确编译foo()的每个实例化? - max66
@max66 当你声明并扩展一个函数模板包时,gcc和clang都可以编译,但是msvc无法编译。当你声明类时,它们都无法编译。在线演示 - 康桓瑋
抱歉,但我非常困惑...我添加了另一个问题。在我看来,鉴于您找到的语句和示例,要么我误解了该语句,要么模板函数有特殊规则,要么g++和clang++都有缺陷。 - max66
@康桓瑋 - 关于函数,你观察到的似乎在我的新问题中得到了证实。无论如何,我越来越困惑了。 - max66
2
@max66 更新了答案。希望现在有所帮助。 - xskxzr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接