问题
I will have the following code snippet in 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 complains when compiling utilA.cpp,
error: variable 'xm::tm time1' has initializer but incomplete type
It seems this is because the utilA.h
is using struct tm
in line 0, but without include the time.h
, and the compiler treat the struct tm
in line 0 as a forward declare, so the struct tm
at line 2 is resolved as xm::tm
inside the header at line 0.
So does the C++ standard define this struct tm
as an type of function parameter as forward declaration? Please help to explain this and quotes from the standard will helpful.
回答1:
In line 0, you declared a class named tm
inside the xm
namespace. Yes, C++ allows declaring types in function/template parameters.
N4140 § 3.4.4 [basic.lookup.elab]/2
If the elaborated-type-specifier is introduced by the class-key and this lookup does not find a previously declared type-name, or if the elaborated-type-specifier appears in a declaration with the form:
class-key attribute-specifier-seqopt identifier;
the elaborated-type-specifier is a declaration that introduces the class-name as described in 3.3.2.
Because you declared a class named tm
inside the xm
namespace, it's the first name that name lookup finds for tm
in line 3. ::tm
(and ::std::tm
) are not considered. And since there's no definition of class ::xm::tm
, the compiler complains about it being an incomplete type.
If you weren't writing C code in C++, you'd write something like1
struct tm;
namespace xz{
void zoo(tm timeval);
}
or
#include <ctime>
namespace xz{
void zoo(tm timeval);
}
and you wouldn't have that problem.
1remember that you cannot forward-declare names in namespace std
回答2:
So does C++ standard define this
struct tm
as an type of function parameter as forward declaration. Please help to explain this and quota from the standard will helpful.
Yes, struct tm timeval
will introduce a new class name xm::tm
here.
(explanations and quotes)
struct tm
is a elaborated type specifier, which could be used to introduce a new class name.
$3.1/4 Declarations and definitions [basic.def]
[ Note: A class name can also be implicitly declared by an elaborated-type-specifier ([dcl.type.elab]). — end note ]
$9.1/2 Class names [class.name]:
A declaration consisting solely of class-key identifier; is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name. It introduces the class name into the current scope.
$3.4.4/2 Elaborated type specifiers [basic.lookup.elab]:
or if the elaborated-type-specifier appears in a declaration with the form:
class-key attribute-specifier-seqopt identifier ;
the elaborated-type-specifier is a declaration that introduces the class-name as described in [basic.scope.pdecl].
$3.3.2/7 Point of declaration [basic.scope.pdecl]:
if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration;
For struct tm timeval
used as function parameter declaration, because <time.h>
is not included and there's still no class named tm
, class tm
will be declared in current scope (i.e. namespace xm
), then xm::tm
will be forward declared.
来源:https://stackoverflow.com/questions/40257011/why-is-this-a-forward-declaration-in-c