【问题标题】:GCC error when typedef name coincides with variadic template parameter nametypedef 名称与可变参数模板参数名称一致时的 GCC 错误
【发布时间】:2020-03-12 10:46:06
【问题描述】:

我偶然发现了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 编译它时,它工作正常。

$ 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's content on templates and typedefs 的大部分内容,但没有找到任何明确解决此案例的内容。我还检查了其他几个问题(12345 等),就我而言,所有这些问题看起来都相似但并不完全适用于这种情况可以说。

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

【问题讨论】:

    标签: c++ c++11 gcc compiler-errors variadic-templates


    【解决方案1】:

    是的,这是一个错误。您所观察到的“它的作用就像 GCC 首先替换了 traits_types 的定义”,然后是 GCC bug 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&lt;T&gt;::types,然后它被依赖名称types 与模板参数包的名称相同而感到困惑。您可以通过给包起一个不同的名称来绕过它,但在标准 C++ 中,从属名称可以与包的名称相同。因为一个必须是合格的,而另一个是不合格的,所以应该没有歧义。

    【讨论】:

    • 太棒了,谢谢!出于我自己的教育目的,您知道标准中是否有什么特别之处可以证明这一点吗?
    • @DavidZ - timsong-cpp.github.io/cppwp/n4659/temp#variadic-5 似乎是最相关的。它基本上说“如果可以将常规模板参数放在那里,这是一个有效的模式”。好吧,当一个常规模板参数是一个嵌套名称时,它并不能保持其作为模板参数的含义。所以包也不应该。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-01
    • 2015-06-04
    相关资源
    最近更新 更多