typedef名称与可变模板参数名称一致时出现GCC错误

GCC error when typedef name coincides with variadic template parameter name

本文关键字:GCC 错误 typedef 参数      更新时间:2023-10-16

我偶然发现了typedef和我想了解的可变模板参数之间的一个奇怪交互。以下代码使用clang编译,但使用GCC时出现错误:

template<typename T> // no error if this is not a template
struct Traits;
#pragma GCC diagnostic ignored "-Wunused-parameter"
template<typename ...args>
void function(args... e) {}
template<typename T>
struct Caller {
typedef typename Traits<T>::types traits_types; // no error if this is changed to a 'using' directive
template<typename ...types> // no error if the pack is converted to a single parameter
static void method(types... e) {
function<traits_types>(e...);
}
};

GCC行为

当我用GCC编译(而不是链接(它时,我在第14行得到一个错误:

$ g++-9.2.0 -Wall -Wpedantic -Wextra -std=c++11 -c test.cpp
test.cpp: In static member function ‘static void Caller<T>::method(types ...)’:
test.cpp:14:31: error: parameter packs not expanded with ‘...’:
14 |         function<traits_types>(e...);
|         ~~~~~~~~~~~~~~~~~~~~~~^~~~~~
test.cpp:14:31: note:         ‘types’
$

它的作用就好像GCC首先替代了traits_types的定义,这将产生

template<typename ...types>
static void method(types... e) {
function<typename Traits<T>::types>(e...);
}

然后评估模板参数替换,此时它将令牌types的最后一次出现视为未扩展的参数包并相应地产生错误。

我已经用GCC 6.4.0、7.3.0、8.2.0、8.3.0、9.1.0和9.2.0以及几个旧版本进行了测试,所有版本的行为都是一致的。

叮当声

然而,当我用clang编译这个时,它工作得很好。

$ clang++-8 -Wall -Wpedantic -Wextra -std=c++11 -c test.cpp
$

这似乎是因为它首先替换了参数包types,然后处理名称traits_types

我在clang 6.0.0、7.0.1和8.0.1以及一些旧版本中一直看到这种行为。

问题

在标准C++11中,GCC给出错误是否正确,或者代码是否有效?还是未定义/实现定义/未指定?

我浏览了cppreference.com关于模板和typedef的大部分内容,但没有找到任何明确解决此问题的内容。我还检查了其他几个问题(1、2、3、4、5等(,据我所知,所有这些问题看起来都很相似,但并不完全适用于这种情况。

如果这实际上是一个编译器错误,那么在错误跟踪器中找到一个相关问题的链接,确认GCC(或clang,如果适用(没有正确处理这个问题,就会很好地解决这个问题。

是的,这是一个错误。您观察到的"就好像GCC首先替代了traits_types的定义">,然后是GCC错误90189:的表现

来源:

struct A {
using CommonName = char;
};
template <typename T, typename... CommonName>
struct B {
using V = typename T::CommonName;
};
template struct B<A>;

Output:
<source>:7:37: error: parameter packs not expanded with '...':
7 |     using V = typename T::CommonName;
|                                     ^
<source>:7:37: note:         'CommonName'
Compiler returned: 1

被所有GCC版本拒绝。接受clang,msvc。

GCC的行为就像您直接编写typename Traits<T>::types一样,然后它会被与模板参数包的名称相同的依赖名称types所混淆。您可以通过给包一个不同的名称来绕过它,但在标准C++中,依赖名称可以与包的名称相同。因为一个必须是合格的,而另一个是不合格的,所以不应该有歧义。