为什么这是C++中的前向声明?

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行中,您声明了一个tmxm命名空间内命名的类.是的,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中描述的类名的声明.

因为您声明了一个tmxm命名空间内命名的类,所以它是名称查找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中转发声明名称


son*_*yao 7

因此,C++标准将此定义struct tm为一种函数参数作为前向声明.请帮助解释这个和标准配额会有所帮助.

是的,这里struct tm timeval将引入一个新的类名xm::tm.


(解释和引用)

struct tm是一个精心设计的类型说明符,可用于引入新的类名.

$ 3.1/4声明和定义[basic.def]

[注意:类名也可以由elaborated-type-specifier([dcl.type.elab])隐式声明. - 结束说明]

$ 9.1/2班级名称[class.name]:

仅由类密钥标识符组成的声明; 是对当前作用域中名称的重新声明,或者是作为类名称的标识符的前向声明.它将类名引入当前范围.

$ 3.4.4/2详细说明的类型说明符[basic.lookup.elab]:

或者如果详细说明类型说明符出现在声明中,其形式如下:

class-key attribute-specifier-seqopt identifier ; 
Run Code Online (Sandbox Code Playgroud)

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声明,然后将被声明为前向声明.