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.
Forward reference is when you declare a type but do not define it. It allows you to use the type by pointer (or reference for C++) but you cannot declare a variable. This is a way to say to the compiler that something exists. Say that you have a Plop structure defined in Plop.
A forward declaration is much faster to parse than a whole header file that itself may include even more header files. Also, if you change something in the header file for class B, everything including that header will have to be recompiled.
The main rule is that you can only forward-declare classes whose memory layout (and thus member functions and data members) do not need to be known in the file you forward-declare it. This would rule out base classes and anything but classes used via references and pointers.
The Google style guide recommends against using forward declarations, and for good reasons: If someone forward declares something from namespace std, then your code exhibits undefined behavior (but will likely work).
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With