【问题标题】:template type name error when same name is used for static member function静态成员函数使用相同名称时模板类型名称错误
【发布时间】:2020-10-17 05:35:07
【问题描述】:

我有以下代码:

struct test
{
    static void T()
    {
    }
    
    template<typename T>
    void f(T* t)
    {
    }
    
    template<typename T>
    T* get()
    {
        return new T();     
    }
};

int main()
{
    test t;
    t.f(t.get<int>());
    
    return 0;
}

这段代码编译得很好,但是如果我将定义移到类之外它不会:

struct test
{
    static void T();
    
    template<typename T>
    void f(T* t);
    
    template<typename T>
    T* get();
};

void test::T()
{
}

template<typename T>
void test::f(T* t)
{
}

template<typename T>
T* test::get()
{
    return new T();     
}

int main()
{
    test t;
    t.f(t.get<int>());
    
    return 0;
}

gcc 错误信息:


#1 with x86-64 gcc 9.3
<source>:17:14: error: variable or field 'f' declared void
   17 | void test::f(T* t)
      |              ^
<source>:17:15: error: expected primary-expression before '*' token
   17 | void test::f(T* t)
      |               ^
<source>:17:17: error: 't' was not declared in this scope
   17 | void test::f(T* t)
      |                 ^
Compiler returned: 1

clang 错误信息:

#1 with x86-64 clang 8.0.0
<source>:17:12: error: variable has incomplete type 'void'
void test::f(T* t)
           ^
<source>:17:17: error: use of undeclared identifier 't'
void test::f(T* t)
                ^
<source>:17:19: error: expected ';' at end of declaration
void test::f(T* t)
                  ^
                  ;
<source>:18:1: error: expected unqualified-id
{
^
<source>:24:16: error: unknown type name 'T'
    return new T();
               ^
5 errors generated.

Compiler returned: 1

MSVC 2019 错误消息:

#1 with x64 msvc v19.24
example.cpp

<source>(17): error C2065: 't': undeclared identifier
<source>(17): error C2182: 'f': illegal use of type 'void'
<source>(17): error C2350: 'test::f' is not a static member
<source>(17): note: see declaration of 'test::f'
<source>(17): error C2513: 'test::f': no variable declared before '='
<source>(18): error C2447: '{': missing function header (old-style formal list?)
Compiler returned: 2

如果将test::f 中的typename T 更改为typename SomeOtherName,或者我将static void T() 重命名为其他名称,它也可以正常编译。

你能解释一下为什么第一个版本可以编译而第二个版本没有吗?你能指出这个错误的标准措辞吗?

编辑: 我已经发布了来自不同编译器的错误消息。正如@cigien 指出的 clang trunk 编译第二个版本

【问题讨论】:

  • clang 可以。我猜这是一个错误。 (虽然不知道谁是正确的)。
  • 第一条评论中链接中的那个。是铿锵的树干。嗯,它不适用于 clang 10.0,所以可能允许它是 clang 主干中的一个错误。
  • 认为 发生在这里的是,由于 test:: 在函数的外联定义中出现在 template&lt;typename T&gt; 之后,因此范围test 声明值T 出现在声明类型模板参数T 的作用域之前,在搜索名称T 的含义时,所以T* t 被认为是指T * t:一个值的乘积通过t。然后,t 的名称查找失败,并且您还会收到错误,因为将 T * t 视为表达式会使 void test::f(T * t) 看起来像 void 类型的变量,然后函数体也没有要附加的函数。
  • @cigien hm,clang trunk 仍然可以从该问题中找到一个基本名称。我现在不太确定这是否是一个重复
  • @cigien 他们是 tryna 在 Clang 11 中实现 clang.llvm.org/cxx_dr_status.html#458,但做错了

标签: c++ language-lawyer


【解决方案1】:

名称查找对于模板参数是非常微妙的:它们在作用域中的 优先级 不仅取决于 template-head 出现的位置,还取决于为其设置的实体它提供模板参数。 [temp.local]/7 说

在出现在类模板定义之外的类模板成员的定义中,类模板成员的名称隐藏了任何封闭类的模板参数的名称模板(如果成员是类或函数模板,则不是成员的 模板参数)。

这在此处并不适用(test 不是类模板),但它表明您的外线定义应该 没问题。编译器知道这一点并不完全是微不足道的,因为它需要识别成员才能确定它是否是成员模板。但是,通过仔细考虑 declarator-id 的哪一部分使用来自每个 template-head 的模板参数,可能的,因此规则应该' t 自动被视为有缺陷; Clang(主干!)似乎在这种扩展的意义上正确地应用了它,尽管(正如a comment 中指出的那样,还有一个旧的 CWG 问题暗示了对这种情况的相反解释。

【讨论】:

  • 这条规则只适用于类模板号?
  • test 不是模板,因此 [temp.local]/7 不适用。另请参阅wg21.link/cwg458,CWG 认为此类代码格式不正确(2004 年 3 月会议的注释)。当前在 Clang 中继中实施 DR458 的尝试不正确。
  • GCC 并没有真正实施这条规则:godbolt.org/z/Hkhk2c
  • 你说得对,规则不适用,但我不确定 CWG458 的逻辑:成员模板的模板参数具有更高的优先级是没有意义的比封闭类的成员 template 但不是封闭类的成员。 (该规则也没有提及类模板本身的模板参数,这些参数可能已在外联定义中更改以匹配类成员。)我将编辑以对冲一点,因为这是一个混乱。
  • 鉴于 CWG458 明确指出非模板案例应该是格式错误的,这看起来不像是一个忽视。是否有任何反射器讨论或高于 CWG458 决定的内容?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-15
  • 2021-02-13
  • 1970-01-01
  • 2015-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多