当 typedef 名称与可变参数模板参数名称一致时出现 GCC 错误
GCC error when typedef name coincides with variadic template parameter name
我偶然发现了 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 编译(而不是 link)时,第 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 给出的错误是否正确,或者代码是否有效?还是 undefined/implementation-defined/otherwise 未指定?
我浏览了很多 cppreference.com's content on templates and typedefs without finding anything that clearly addresses this case. I also checked several other questions (, 2, 3, , ,等等),所有这些看起来都很相似,但据我所知并不完全适用于这种情况。
如果这实际上是一个编译器错误,link 到错误跟踪器中的相关问题确认 GCC(或 clang,如果适用)没有正确处理这个问题会很好地解决这个问题.
是的,这是一个错误。您观察到的 "it acts as if GCC first substitutes in the definition of traits_types" 之后会出现 GCC bug 90189:
Source:
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
Rejected by all GCC versions. Accepted by clang, msvc.
GCC 就像你直接写 typename Traits<T>::types
一样,然后它会因为依赖名称 types
与模板参数包的名称相同而感到困惑。您可以通过给包一个不同的名称来绕过它,但在标准 C++ 中,从属名称可以与包的名称相同。因为一个必须合格,一个不合格,所以不能有歧义。
我偶然发现了 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 编译(而不是 link)时,第 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 给出的错误是否正确,或者代码是否有效?还是 undefined/implementation-defined/otherwise 未指定?
我浏览了很多 cppreference.com's content on templates and typedefs without finding anything that clearly addresses this case. I also checked several other questions (
如果这实际上是一个编译器错误,link 到错误跟踪器中的相关问题确认 GCC(或 clang,如果适用)没有正确处理这个问题会很好地解决这个问题.
是的,这是一个错误。您观察到的 "it acts as if GCC first substitutes in the definition of traits_types" 之后会出现 GCC bug 90189:
Source:
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
Rejected by all GCC versions. Accepted by clang, msvc.
GCC 就像你直接写 typename Traits<T>::types
一样,然后它会因为依赖名称 types
与模板参数包的名称相同而感到困惑。您可以通过给包一个不同的名称来绕过它,但在标准 C++ 中,从属名称可以与包的名称相同。因为一个必须合格,一个不合格,所以不能有歧义。