Why doesn't this C++ code give compilation error?

狂风中的少年 提交于 2019-12-10 16:26:54

问题


#include<iostream>
using namespace std;

struct a{
    int e;
    struct abc *d;
};

struct abc{
    int c;
};

int main()
{
 return 0;  
}

I have defined the struct abc after definition of struct a in which i have declared a structure pointer for abc. This is supposed to throw compilation error because abc is used before its declaration. But, it doesn't, why? Whereas when i replace it with just struct abc d instead of struct abc *d, it is giving compilation error as expected.


回答1:


This declaration

struct abc *d;

on the one hand declares struct abc and on the other hand declares pointer d of type struct abc *.

In this declaration there is no need to have the exact definition of struct abc because no data member of the structure is used.

This specifier struct abc is called elaborated type specifier.

It introduces a new type in the given scope or refers to an already declared type.




回答2:


You're right in that, usually, you'd need a forward declaration of such a type:

// Forward declaration
struct bar;

struct foo
{
    bar* d;   // only a pointer-to-bar; forward declaration sufficient
};

struct bar
{
    int c;
};

However, you are (for some reason) using the antiquated idiom of writing struct before the type name. (This was required in C but has never been in C++.)

struct foo
{
    struct bar* d;
};

struct bar
{
    int c;
};

Because you write struct bar there instead of just bar, that itself counts as a forward declaration of sorts. The compiler now knows that bar is a type and that's all it needs to know.

It's a bit obscure and subtle, but that's why you do not need the prior forward declaration any more.

[C++11: 3.1/4]: [ Note: A class name can also be implicitly declared by an elaborated-type-specifier (7.1.6.3). —end note ]

[C++11: 3.3.2/6]: The point of declaration of a class first declared in an elaborated-type-specifier is as follows:

  • for a declaration of the form

       class-key attribute-specifier-seqopt identifier ;

    the identifier is declared to be a class-name in the scope that contains the declaration, otherwise

  • for an elaborated-type-specifier of the form

       class-key identifier

    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; otherwise, except as a friend declaration, the identifier is declared in the smallest namespace or block scope that contains the declaration. [ Note: These rules also apply within templates. —end note ] [ Note: Other forms of elaborated-type-specifier do not declare a new name, and therefore must refer to an existing type-name. See 3.4.4 and 7.1.6.3. —end note ]

[C++11: 3.4.4/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.




回答3:


It works, because the compiler has all the information it needs:

  • the size of d (= size of pointer)1, and
  • what abc is (it's a struct).

If you stored an object of type struct abc instead:

struct abc d;

it would result in an error because the information about the size and memory layout of d is missing (because struct abc hasn't been defined yet).

Also if you left out the struct keyword:

abc *d;

then the information about what abc is would be missing, and it would be an error as well.


This is supposed to throw compilation error because abc is used before its declaration.

It is not used, it is only declared. Using abc via a pointer would require dereferencing the pointer first (and if abc is not defined at that point, it would be an error).


1 Pointers to incomplete types are allowed because the size and memory layout of a pointer doesn't depend on what it points to.




回答4:


Pointer declared to incomplete types are allowed .

This is forward declaration-

Forward declaration is a declaration preceeding an actual definition, usually for the purpose of being able to reference the declared type when the definition is not available. Of course, not everything may be done with the declared-not-defined structure, but in certain context it is possible to use it. Such type is called incomplete.

A declaration of the following form

  class-key attr identifier ;   // struct abc *d; (Your case)

Declares a class type which will be defined later in this scope. Until the definition appears, this class name has incomplete type.

When struct a is declared it doesn't know the specs of struct abc yet, but you can forward reference it.




回答5:


In struct a you refer to a pointer to struct abc, so the compiler has no need for more information about it to compute the size of the d member.

In a later stage, it will check that struct abc is defined, if needed (for exemple if it is dereferenced). This explains the rationale for the standard which

  • forbids to declare variables of unknown type (abc, when not forwarded, is such an unknown type)

  • allows to declare them as pointers to incomplete type. (struct abc is an incomplete type: at least it is known to be some struct).

Practically, declaring a structure as

 struct a {
    struct abc *d;
 }

amounts to forwarding the declaration of (struct) type abc, as in

struct abc;           // forward
struct a {
   abc *d;            // legal
};

However the type abc is incomplete, so the following is illegal

struct z {
   struct abc y;   // error : incomplete struct type
}

For the curiosity, this is OK:

struct A {
   struct B* ptr1;    // forwards declaration of B, and use it
   B* ptr2;           // B is a known (incomplete-)type name now
} 


来源:https://stackoverflow.com/questions/31875710/why-doesnt-this-c-code-give-compilation-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!