But of course we shouldn't even think of doing such things, I know, but still this is quite interesting:
class A; //declaration
struct A {...}; //definition
struct B; //declaration
class B {...}; //definition
When I think about it, I don't see any problems if such a thing were really allowed(because struct and class are essentially the same thing). But is it (standard-wise)?
MSVC accepts and compiles it, with a warning.
It is allowed according to the standard, but as some compilers warn about it, it is not very useful.
I believe the warning is/was caused by MSVC using a different name mangling for structs and classes, which would make it even less useful...
On request from #Armen:
7.1.5.3 Elaborated type specifiers, p3
... in any elaborated-type-specifier, the enum keyword shall be used to refer to an enumeration (7.2), the union class-key shall be used to refer to a union (clause 9), and either the class or struct class-key shall be used to refer to a class (clause 9), declared using the class or struct class-key.
As per C++03 Standard 9.1 - 2
"A class definition introduces the class name into the scope where it is defined and hides any class, object, function, or other declaration of that name in an enclosing scope (3.3)."
So it is valid as per the standard.
Playing around a bit with the example code:
#include<iostream>
class A; //declaration
struct A { int i;}; //definition
struct B; //declaration
class B {int j;}; //definition
int main()
{
A obj;
obj.i = 10;
B obj2;
obj2.j = 10;
std::cout<<"sizeof"<<sizeof(struct B);
return 0;
}
Here is the output:
prog.cpp: In function ‘int main()’:
prog.cpp:6: error: ‘int B::j’ is private
prog.cpp:13: error: within this context
The only difference between C++ struct & class is that default access specifier for structure is public while for class it is private.
So, From the above example:
In this case compiler treats A as a structure &
B as a class
As you see, the compiler follows the quote from the standard and the type with the definition is what the compiler recognizes, over the declaration type.
Related
Code example:
struct A {};
struct B { using A = A; };
int main()
{
B b;
}
Clang compiles it. But GCC gives the following error (demo):
declaration of 'using A = struct A' changes meaning of 'A'
The C++ standard says:
If a class name ([class.name]) or enumeration name ([dcl.enum]) and a variable, data member, function, or enumerator are declared in the same declarative region (in any order) with the same name (excluding declarations made visible via using-directives ([basic.lookup.unqual])), the class or enumeration name is hidden wherever the variable, data member, function, or enumerator name is visible.
UPD.0: thanks to Vlad from Moscow
A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule
Does that mean that GCC behavior is incorrect? Thanks!
It seems that it is a bug of gcc. According to the C++ 20 Standard (6.3.7 Class scope)
2 A name N used in a class S shall refer to the same declaration in
its context and when re-evaluated in the completed scope of S. No
diagnostic is required for a violation of this rule.
In this case
struct A {};
struct B { using A = A; };
the name B::A refers to the same declaration of struct A.
Here is an example from the C++ Standard that shows the meaning of the quote.
typedef char* T;
struct Y {
T a; // error: T refers to ::T but when reevaluated is Y::T
typedef long T;
T b;
};
I wrote following simple program & compiled it on gcc compiler
#include <stdio.h>
typedef int i;
void foo()
{
struct i {i i;} i;
i.i = 3;
printf("%i\n", i.i);
}
int main() { foo(); }
It compiles & runs fine in C.(See live demo here) But it fails in compilation in C++. C++ compiler gives following error messages.
prog.cc: In function 'void foo()':
prog.cc:5:17: error: field 'i' has incomplete type 'foo()::i'
struct i {i i;} i;
^
prog.cc:5:12: note: definition of 'struct foo()::i' is not complete until the closing brace
struct i {i i;} i;
See live demo here
I couldn't find rules regarding this in C & C++ standards. Why it compiles fine in C but not in C++ ? What does the standard says about this ? I very well know that C & C++ are different languages having different rules but I am curious to know about exact rules.
The difference between C and C++ is the following. In C the data member i is considered as having type int because if you wanted that it had type struct i then you have to write struct i i specifying the keyword struct before i.
Structure tags are in their own namespace compared with the namespace of other variables.
According to the C Standard (6.2.3 Name spaces of identifiers)
1 If more than one declaration of a particular identifier is visible
at any point in a translation unit, the syntactic context
disambiguates uses that refer to different entities. Thus, there are
separate name spaces for various categories of identifiers, as
follows:
— label names (disambiguated by the syntax of the label declaration
and use);
— the tags of structures, unions, and enumerations (disambiguated by
following any32) of the keywords struct, union, or enum);
— the members of structures or unions; each structure or union has a
separate name space for its members (disambiguated by the type of the
expression used to access the member via the . or -> operator);
— all other identifiers, called ordinary identifiers (declared in
ordinary declarators or as enumeration constants).
As for C++ then inside the structure definition the name of the structure hides the name of the typedef and the compiler issues the error. In C++ there is separate class scope.
For example in C++ (3.4 Name lookup) there is written
3 The injected-class-name of a class (Clause 9) is also considered
to be a member of that class for the purposes of name hiding and
lookup.
and (3.4.1 Unqualified name lookup)
7 A name used in the definition of a class X outside of a member
function body or nested class definition29 shall be declared in one of
the following ways: — before its use in class X or be a member of
a base class of X (10.2), or ...
Thus the injected name of the class hides the typedef name within the class definition.
Take into account that outside the class definition the name of the class can be hidden by the same name of an object. Thus if you want to declare an object of the class in that scope you have to use its elaborated name like
int i;
struct i {};
//...
struct i obj;
I tried to use incomplete type in nested name specifier as the following:
class A;
int b= A::c; // error: incomplete type ‘A’ used in nested name specifier
class A {
static const int c=5;
};
There is says nothing about it in the 3.4.3/1 of N3797 working draft:
The name of a class or namespace member or enumerator can be referred
to after the :: scope resolution operator (5.1) applied to a
nested-name-specifier that denotes its class, namespace, or
enumeration
So is that behavior implementation dependent?
Introduction
There are several places in the standard that implicitly implies that your code is ill-formed, but the below quotation speaks for itself:
3.3.2p6 Point of declaration [basic.scope.pdecl]
After the point of declaration of a class member, the member name can be looked up in the scope of its class.
The problem with your code isn't that you try to reach inside the body of an incomplete type, the problem is that you can only refer to a class member name after it has been declared.
Since your forward-declaration (of course) doesn't introduce any member named c, it is ill-formed to refer to such name.
The misleading diagnostic...
The diagnostic issued by both gcc and clang when being fed your code is somewhat misleading, and honestly I feel a bug report is in order.
foo.cpp:3:8: error: incomplete type 'A' named in nested name specifier
We are allowed to name an incomplete type in a nested-name-specifier, but as said; we are not allowed to refer to a member that has not yet been declared.
ill-formed:
class X {
static int a[X::x]; // ill-formed, `X::x` has not yet been declared
static int const x = 123;
};
legal:
class X {
int const x = 123;
int a[X::x]; // legal, `X` is incomplete (since we are still defining it)
// but we can still refer to a _declared_ member of it
};
I got the same error when accessing a member variable of a class that had been forward-declared in the header, but not included in the main from where I accessed it.
myClass.h
class FowardInclude; // has member "int x", but only forward-declared here
class MyClass {
public:
void foo(int num);
}
main.cpp
#include "forwardInclude.h" // <-- This is the line I was missing
#include "myClass.h"
MyClass m;
m.foo(ForwardInclude::x);
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Difference between ‘struct’ and ‘typedef struct’ in C++?
An answer to this question led me to wonder about the following:
I presume that defining a class as follows:
typedef class {int i;} C;
would be completely equivalent to defining it in the conventional manner:
class C
{
int i;
};
Is this presumption correct?
In this isolated example they are functionally the same, at least from the outside.
However there are differences. One instance in particular, you cannot declare a constructor for a struct or a class declared in this way, simply because the class is unnamed. Similarly you cannot declare any function that involves the class' name. Here are some examples:
typedef class
{
public:
Gizmo() : n_(42) {}; // NOT OK
~Gizmo();
Gizmo& operator<<(int n);
private:
int n_;
} Gizmo;
You also cannot forward declare an anonymous class:
class Gizmo;
In C++ I have never seen a case where typedefing an anonymous struct or a class is preferable to simply declaring a class or a struct that is named. In some cases the traditional method is definitely preferred. The moral of the story is: don't use typedef class {} Name; in C++. It buys you nothing, and costs you something.
I believe this is a duplicate question (can't find), but if not, observe that this compiles:
class C
{
int i;
};
void C() {}
class C x;
while this won't:
typedef class
{
int i;
} C;
void C() {}
C x;
The name spaces are different.
From a practical standpoint yes, because the standard says (9.1/5) that
A typedef-name (7.1.3) that names a class type, or a cv-qualified version thereof, is also > a class-name. If a typedef-name that names a cv-qualified class type is used where a
class-name is required, the cv-qualifiers are ignored.
7.1/3 says:
A name declared with the typedef specifier becomes a typedef-name.
Within the scope of its declaration, a typedef-name is syntactically
equivalent to a keyword and names the type associated with the
identifier in the way described in Clause 8. A typedef-name is thus a
synonym for another type.
From a theoretical standpoint no, because you could (and indeed I see people already have) draft programs that are valid or invalid depending on which version was used, since 7.1/3 continues from where I cut it off to say:
A typedef-name does
not introduce a new type the way a class declaration (9.1) or enum declaration does.
They're not equivalent. In particular,
int main()
{
class C c;
}
will only compile for one of the two definitions.
Here is yet another difference: the latter can be forward-declared, the former can't.
This is related to a recent question.
Basically the following code:
class A
{
class B* b;
B* c;
};
compiles although class B is not declared or forward-declared. Is this syntax equivalent to a forward declaration? Are there any differences?
You can declare a type and an object in the same declaration.
class B* b;
Declares a type, B and an object b which has type pointer to B. The type is incomplete and is looked up in the scope in which it occurs, if the lookup fails to find an existing declaration for the class then the type names a type in the nearest enclosing namespace scope (strictly non-class non-function-prototype scope, which is usually a namespace). The object is a member of the scope in which the declaration appears (in this case, class A).
In most cases it's more common to declare a complete type and an object together, in this case the type is sometimes left anonymous. E.g.
struct { int a; int b; } x;
The relevant parts of the standard for the name scoping rules are 7.1.5.3 [dcl.type.elab] Elaborated type specifiers / 2 and the referenced sections in 3.4.4 and 3.3.1 :
3.4.4 describes how name lookup proceeds for the identifier in an elaborated-type-specifier. If the identifier resolves to a class-name or enum-name, the elaborated-type-specifier introduces it into the declaration the same way a simple-type-specifier introduces its type-name. If the identifier resolves to a typedef-name or a template type-parameter, the elaborated-type-specifier is ill-formed. [ ... ] If name lookup does not find a declaration for the name, the elaborated-type-specifier is ill-formed unless it is of the simple form class-key identifier in which case the identifier is declared as described in 3.3.1.
No, it's a declaration, of a pointer to B. You are not declaring B here, only a pointer to it, and there's nothing forward about it.
Edit: I was wrong, sorry. See other answer.
I would like to add few details to answer of Charles Bailey:
class A
{
public:
class B * b;
class C {} *c;
int d;
} a;
B* globalB;
// C* globalC; identifier "C" is undefined here
int main(int argc, char *argv[])
{
a.d = 1;
cout << a.d;
}
Yes, it defines incomplete type B and b as a pointer to B at once. But here comes the fun:
"An exception to the scope visibility of a nested class declaration is when a type name is declared together with a forward declaration. In this case, the class name declared by the forward declaration is visible outside the enclosing class, with its scope defined to be the smallest enclosing non-class scope." (Nested Class Declarations)
Which means that type B is defined out of scope A which allows you to define variable globalB. If you don't want B to be defined out of scope A, you can use {} just like it is used with type C in my example.
By the way, this example is correct and its output is: 1