我将在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
定义为一种函数参数作为前向声明?请帮助解释一下,标准中的引用会有所帮助。
答案 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 tm
是elaborated 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
将被声明。