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

时间:2016-10-26 08:14:25

标签: c++ language-lawyer standards forward-declaration

我将在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
     }
}

GCC在编译utilA.cpp时抱怨,

error: variable 'xm::tm time1' has initializer but incomplete type

这似乎是因为utilA.h在第0行使用struct tm,但没有包含time.h,编译器将第0行中的struct tm视为转发声明,因此第2行的struct tm在第0行的标题内解析为xm::tm

C ++标准是否将此struct tm定义为一种函数参数作为前向声明?请帮助解释一下,标准中的引用会有所帮助。

2 个答案:

答案 0 :(得分:20)

在第0行中,您在tm命名空间内声明了一个名为xm的类。是的,C ++允许在函数/模板参数中声明类型。

  

N4140§3.4.4[basic.lookup.elab] / 2

     

如果 class-key 引入了 elaborated-type-specifier ,   此查找找不到先前声明的类型名称,或者如果是    elaborated-type-specifier 出现在声明中,格式为:

     

class-key attribute-specifier-seq opt identifier ;

      elaborated-type-specifier 是一个引入的声明   class-name,见3.3.2。

因为您在tm命名空间中声明了一个名为xm的类,所以它是名称查找在第3行tm中找到的第一个名称::tm(和{{ 1}})不予考虑。由于没有类::std::tm的定义,编译器会抱怨它是一个不完整的类型。

如果您不是用C ++编写C代码,那么您可以编写类似 1

的内容
::xm::tm

struct tm;

namespace xz{
    void zoo(tm timeval);
}

你不会有这个问题。

1 请记住,您无法在命名空间std

中转发声明名称

答案 1 :(得分:7)

  

C ++标准也将此struct tm定义为一种函数参数作为前向声明。请帮助解释一下这个标准中的配额会有所帮助。

是的,struct tm timeval会在此处引入新的类名xm::tm

(解释和引用)

struct tmelaborated type specifier,可用于引入新的类名。

$3.1/4 Declarations and definitions [basic.def]

  

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

$9.1/2 Class names [class.name]

  

仅由类密钥标识符组成的声明;是一个   重新声明当前范围内的名称或转发   将标识符声明为类名。它介绍了这门课程   命名为当前范围。

$3.4.4/2 Elaborated type specifiers [basic.lookup.elab]

  

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

class-key attribute-specifier-seqopt identifier ; 
     

elaborated-type-specifier是一个引入的声明   [basic.scope.pdecl]中描述的类名。

$3.3.2/7 Point of declaration [basic.scope.pdecl]

  

如果在decl-specifier-seq或中使用了elaborated-type-specifier   在命名空间作用域中定义的函数的parameter-declaration-clause,   标识符在名称空间中声明为类名   包含声明;

对于用作函数参数声明的struct tm timeval,因为未包含<time.h>并且仍然没有名为tm的类,所以将在当前范围内声明类tm(即名称空间xm),然后xm::tm将被声明。