静态成员函数使用相同名称时模板类型名称错误

Mir*_*pas 8 c++ language-lawyer

我有以下代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

叮当错误信息:

#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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

如果它更改typename Ttypename SomeOtherNameintest::f或者我重命名static void T()为其他名称,它也可以很好地编译。

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

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

Dav*_*ing 1

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

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

这并不真正适用于这里(test不是类模板),但它表明您的外线定义应该没问题。对于编译器来说,知道这一点并非完全无关紧要,因为它需要识别成员才能决定它是否是成员模板。但是,通过仔细考虑declarator-id的哪一部分使用每个template-head中的模板参数可能的,因此该规则不应自动被视为有缺陷;Clang(trunk!)似乎在这种扩展意义上正确应用了它,尽管(正如评论中指出的,还有一个旧的 CWG 问题建议对此案例进行相反的解释。