C++ typedef名称与可变模板参数名称一致时发生GCC错误
我无意中发现了C++ typedef名称与可变模板参数名称一致时发生GCC错误,c++,c++11,gcc,compiler-errors,variadic-templates,C++,C++11,Gcc,Compiler Errors,Variadic Templates,我无意中发现了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 functio
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...);
}
};
然后评估模板参数替换,此时它将最后出现的标记types
视为未展开的参数包,并相应地产生错误
我已经用GCC 6.4.0、7.3.0、8.2.0、8.3.0、9.1.0和9.2.0以及几个旧版本对此进行了测试,所有版本的行为都是一致的
叮当声
然而,当我用叮当声编译它时,它工作得很好
$ clang++-8 -Wall -Wpedantic -Wextra -std=c++11 -c test.cpp
$
这似乎是它首先替换了参数packtypes
,然后处理名称traits\u types
我在Clang6.0.0、7.0.1和8.0.1以及一些旧版本中都看到了这种行为
问题:
在标准C++11中,GCC给出的错误是正确的,还是代码有效?或者是未定义/实现定义/未指定
我查阅了很多资料,但没有找到任何能清楚说明这个问题的资料。我还检查了其他几个问题(,,等等),所有这些问题看起来都很相似,但据我所知,并不完全适用于这种情况
如果这实际上是一个编译器错误,那么在错误跟踪器中找到一个相关问题的链接,确认GCC(或clang,如果适用)没有正确处理这个问题,将很好地解决这个问题。是的,这是一个错误。您所观察到的“它的行为就像GCC在特征类型的定义中第一次替代一样”,随后是以下表现: 资料来源:
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
结构A{
使用CommonName=char;
};
模板
结构B{
使用V=typename T::CommonName;
};
模板结构B;
输出:
:7:37:错误:参数包未使用“…”展开:
7 |使用V=typename T::CommonName;
| ^
:7:37:注意:“CommonName”
返回的编译器:1
被所有GCC版本拒绝。已被msvc的clang接受
GCC的行为就像您直接编写了
typename Traits::types
,然后它会因为依赖名称types
与模板参数包的名称相同而混淆。您可以通过给包不同的名称来绕过它,但是在标准C++中,依赖名可以与包的名称相同。因为一个必须合格,而另一个不合格,所以不应该有歧义。太棒了,谢谢!出于我自己的教育目的,你知道标准中是否有什么特别的东西可以证明这一点吗?@DavidZ-似乎是最中肯的。它基本上说“如果可以在那里放置一个常规模板参数,那么这是一个有效的模式”。嗯,当常规模板参数是嵌套名称时,它不会保留其作为模板参数的含义。因此,这群人也不应该这样做。
$ clang++-8 -Wall -Wpedantic -Wextra -std=c++11 -c test.cpp
$
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