代码之家  ›  专栏  ›  技术社区  ›  Jan Schultke

你曾经需要把typename和template放在一起吗?

  •  1
  • Jan Schultke  · 技术社区  · 2 年前

    我很好奇是否有这样的情况 typename 就其本身而言并不能充分消除歧义。 当 类别名 如果使用消歧器,则执行以下操作 合格id 必须是类型。 例如,在以下代码中:

    template <typename T>
    struct foo;
    
    template <typename T, typename U>
    void bar() {
        typename foo<T>::mystery<0> x;
    }
    

    该标准要求使用 template 对于从属名称 mystery ,但如果您省略它,我不会意识到解析的歧义 <0 不可能是“小于零”的意思,因为 类别名 说我们必须产生一个类型,而表达式永远不会产生类型。

    GCC compiles this, but clang doesn't 并产生错误:

    error: use 'template' keyword to treat 'mystery' as a dependent template name
        typename foo<T>::mystery<0> x;
                         ^
                         template 
    

    我的理解正确吗。 样板 是标准要求的,但对编译器来说不是必需的?

    使现代化

    在C++23中似乎只对终端名称有一些放宽 神秘的事物 ,这可能允许省略 类别名 但这些都与海湾合作委员会的实施无关。C++98模式下的GCC 10允许:

    template <typename T>
    void bar() {
        typename foo<T>::mystery<T>::surprise<T>::shock<T> x;
    }
    

    在中找不到此功能 changelogs

    即使使用任意嵌套 样板 在这里是不必要的,因为在我们解析 id表达式 声明人 每一个 < 必须是一个模板尖括号。

    0 回复  |  直到 1 年前
        1
  •  0
  •   Jan Schultke    1 年前

    简言之,Clang还没有实施所有的放松措施,GCC过于宽容,让你省略 template 。 许多关于作用域、名称查找、从属名称等的规则都被 P1787: Declarations and where to find them

    就目前情况来看, [temp.names] p3 状态:

    A. < 被解释为 template-argument-list 如果它跟在一个不是转换函数id的名称后面,并且

    在声明中:

    typename foo<T>::mystery<0> x;
    

    mystery 位于 仅类型上下文 typename-specifier ,所以 <0> 被解释为 模板参数列表 ,而不是 < > 操作员。 这个 样板 关键字可以在这里安全地省略,但Clang还并没有实现这一点。

    GCC允许省略 样板 它应该是强制性的

    GCC允许:

    typename foo<T>::mystery<T>::surprise<T>::shock<T> x;
    

    它不仅支持早在C++23生效之前的所有放宽,而且该声明也是无效的。

    • typename foo<T>::mystery<T> 可以解析为 类型名说明符 哪里 样板 之前 神秘的事物 是可选的
    • surprise<T>:: 无效,因为 surprise 是中的终端名称 嵌套名称说明符 并且那些被排除在松弛之外(见上文)

    除了标准之外,GCC在直觉上走得太远了,因为 typename foo<T>:神秘<T> ,可以说“ typename 消歧器已用完”。

    推荐文章