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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With