Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template type name error when same name is used for static member function

I have the following code:

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;
}

This code compiles fine as it is, but if I move the definition outside the class it doesn't:

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 error message:


#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 error message:

#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 error message:

#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

It also compiles fine if it change typename T to typename SomeOtherName in test::f or if I rename static void T() to something else.

Can you please explain me why the first version compiles and the second doesn't? Can you point me to the standard wording for this error?

EDIT: I've posted error message from different compilers. As @cigien pointed clang trunk compiles the second version

like image 792
Mircea Ispas Avatar asked Jun 26 '20 15:06

Mircea Ispas


1 Answers

Name lookup is very subtle for template parameters: the priority that they have among scopes depends not just on where the template-head appears but also on the entity for which it provides the template parameters. [temp.local]/7 says

In the definition of a member of a class template that appears outside of the class template definition, the name of a member of the class template hides the name of a template-parameter of any enclosing class templates (but not a template-parameter of the member if the member is a class or function template).

This doesn't literally apply here (test is not a class template), but it suggests that your out-of-line definition should be fine. It's not entirely trivial for the compiler to know that since it needs to identify the member in order to decide whether it's a member template. However, it is possible by carefully considering what part of the declarator-id uses template parameters from each template-head, so the rule shouldn't automatically be considered defective; Clang (trunk!) seems to be applying it correctly in this expanded sense, although (as pointed out in a comment there is also an old CWG issue suggesting the opposite interpretation for this case.

like image 164
Davis Herring Avatar answered Nov 15 '22 00:11

Davis Herring