Zij*_*gWu 18 c++ standards forward-declaration language-lawyer
我将在utilA.cpp中有以下代码片段:
// utilB.h
namespace xm
{
void zoo(struct tm timeval); //<-----line 0
}
// utilA.cpp
#include <utilB.h> //<----line 1
#include <time.h> //<----line 2
namespace xm
{
void foo()
{
struct tm time1 = {0}; //<----line 3
}
}
Run Code Online (Sandbox Code Playgroud)
GCC在编译utilA.cpp时抱怨,
error: variable 'xm::tm time1' has initializer but incomplete type
Run Code Online (Sandbox Code Playgroud)
这似乎是因为它在第0行utilA.h使用struct tm,但没有包括time.h,并且编译器将第struct tm0行视为前向声明,因此第struct tm2 xm::tm行在第0行的头部内被解析.
那么C++标准是否将此struct tm函数参数定义为前向声明?请帮助解释一下,标准中的引用会有所帮助.
krz*_*zaq 20
在第0行中,您声明了一个tm在xm命名空间内命名的类.是的,C++允许在函数/模板参数中声明类型.
N4140§3.4.4[basic.lookup.elab]/2
如果由类键引入了elaborated-type-specifier,并且此查找未找到先前声明的类型名称,或者如果 elaborated-type-specifier出现在具有以下形式的声明中:
class-key attribute-specifier-seq opt identifier ;
在阐述了式说明符是介绍如在3.3.2中描述的类名的声明.
因为您声明了一个tm在xm命名空间内命名的类,所以它是名称查找tm在第3行中找到的第一个名称.::tm(和::std::tm)不被考虑.由于没有类的定义::xm::tm,编译器抱怨它是一个不完整的类型.
如果你不是用C++编写C代码,你会写一些像1的东西
struct tm;
namespace xz{
void zoo(tm timeval);
}
Run Code Online (Sandbox Code Playgroud)
要么
#include <ctime>
namespace xz{
void zoo(tm timeval);
}
Run Code Online (Sandbox Code Playgroud)
你不会有这个问题.
1 记住你不能在名称空间std中转发声明名称
因此,C++标准将此定义
struct tm为一种函数参数作为前向声明.请帮助解释这个和标准配额会有所帮助.
是的,这里struct tm timeval将引入一个新的类名xm::tm.
(解释和引用)
struct tm是一个精心设计的类型说明符,可用于引入新的类名.
[注意:类名也可以由elaborated-type-specifier([dcl.type.elab])隐式声明. - 结束说明]
仅由类密钥标识符组成的声明; 是对当前作用域中名称的重新声明,或者是作为类名称的标识符的前向声明.它将类名引入当前范围.
$ 3.4.4/2详细说明的类型说明符[basic.lookup.elab]:
或者如果详细说明类型说明符出现在声明中,其形式如下:
Run Code Online (Sandbox Code Playgroud)class-key attribute-specifier-seqopt identifier ;elaborated-type-specifier是一个声明,它引入了[basic.scope.pdecl]中描述的类名.
$ 3.3.2/7声明点[basic.scope.pdecl]:
如果在命名空间作用域中定义的函数的decl-specifier-seq或parameter-declaration-clause中使用了elaborated-type-specifier,则在包含声明的命名空间中将标识符声明为类名.
对于struct tm timeval用作函数参数声明,因为<time.h>没有包含并且仍然没有命名的tm类,class tm将在当前作用域(即名称空间xm)中xm::tm声明,然后将被声明为前向声明.