【发布时间】: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<typename T>之后,因此范围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