Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this a forward declaration in C++?

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.

like image 462
ZijingWu Avatar asked Oct 26 '16 08:10

ZijingWu


People also ask

What is meant by a forward reference in C?

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.

Why forward-declare instead of include?

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.

When can you forward-declare?

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.

Is forward declaration good practice?

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).


2 Answers

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

like image 77
krzaq Avatar answered Oct 17 '22 17:10

krzaq


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.

like image 7
songyuanyao Avatar answered Oct 17 '22 19:10

songyuanyao