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);
Related
What happens when:
I declare and define a name X (either object or type) in the global scope.
I start writing a class. Inside the class, but outside of function bodies etc., I use X.
Later in the class, I declare name X again.
On the Class Scope page on cppreference.com, this is considered undefined behavior. The code snippet looks like this:
typedef int c; // ::c
enum { i = 1 }; // ::i
class X {
char v[i]; // Error: at this point, i refers to ::i
// but there is also X::i
int f() {
return sizeof(c); // OK: X::c, not ::c is in scope inside a member function
}
char c; // X::c
enum { i = 2 }; // X::i
};
typedef char* T;
struct Y {
T a; // Error: at this point, T refers to ::T
// but there is also Y::T
typedef long T;
T b;
};
But in Chapter 3.1 of Stanley B. Lippman's book Inside the C++ Object Model, the compiler should raise an error.
His comments:
In the following code fragment, for example, the type of length in both
member function signatures resolves to that of the global typedef—that is, to int. When the subsequent declaration of the nested typedef of length is encountered,
the Standard requires that the earlier bindings be flagged as illegal
His code snippet looks like this:
typedef int length;
class Point3d {
public:
// oops: length resolves to global
// ok: _val resolves to Point3d::_val
void mumble( length val ) { _val = val; }
length mumble() { return _val; }
private:
// length must be seen before its first reference within the class.
// This declaration makes the prior reference illegal.
typedef float length;
length _val;
};
I tested with clang 7.0.0, there is no warning or error, and the length seems to bind to int. I understand that compiler testing results cannot be used to analyze UBs, so I'm asking this question.
Who is right? Or if they are both right, what am I missing? What does the current standard say about this?
The question already points to the proper cppreference snippet [Class Scope]:
The potential scope of a name declared in a class begins at the point of declaration and includes the rest of the class body and all function bodies ...
Then on the same cppreference page:
If a name is used in a class body before it is declared, and another declaration for that name is in scope, the program is ill-formed, no diagnostic required.
According to the above it sounds like gcc is right with the error and clang is also right (as no diagnostic is required) but is being too permissive allowing ill formed code to compile.
The relevant wording in the spec [basic.scope.pdecl] 6.4.2/1 - Point of Declaration:
... The point of declaration for an enumeration is immediately after the identifier (if any) in either its enum-specifier ([dcl.enum]) or its first opaque-enum-declaration ([dcl.enum]), whichever comes first. The point of declaration of an alias or alias template immediately follows the defining-type-id to which the alias refers.
And [basic.scope.declarative] 6.4.1/4.2:
exactly one declaration shall declare a class name or enumeration name that is not a typedef name and the other declarations shall all refer to the same variable, non-static data member, or enumerator, or all refer to functions and function templates; in this case the class name or enumeration name is hidden ([basic.scope.hiding]).
Then [basic.scope.class] 6.4.7/2 - Class scope:
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.
Bottom line: this code is ill formed (and thus if compiled can be viewed as undefined behavior), however the compiler can ignore it as no diagnostic is required.
They're both right.
As the article you referenced states, this does not have undefined behaviour, but is rather ill-formed, no diagnostic required.
This is a very specific phrase in the standard, with a very specific meaning, which correlates to exactly what you've seen: the program is ill-formed, but the implementation doesn't have to diagnose that if it doesn't want to.
So, you may get an error, you may not. Everything's fine.
But fix your code. 🙂
This is a follow-up question.
In the previous question, #JohannesSchaub-litb said that the following code is not fully standard-conformant:
class { int i; }; //unnamed-class definition. § 9/1 allows this!
and then he added,
while it is grammatically valid, it breaks the rule that such a class must declare at least one name into its enclosing scope.
I couldn't really understand this. What name is he talking about?
Could anyone elaborate on this further (preferably quoting the Standard)?
Clause 9 of the standard allows class {public: int i;} (note the lack of a final semicolon) because this decl-specifier-seq for an unnamed class might be used in some other construct such as a typedef or a variable declaration. The problem with class {public: int i;}; (note that the final semicolon is now present) is that this class specification now becomes a declaration. This is an illegal declaration per clause 7, paragraph 3 of the standard:
In such cases, and except for the declaration of an unnamed bit-field (9.6), the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration.
The point is that by declaring class{ int i; }; you are assembling a bunch of symbol (int i, in this case) you will not be able to use anywhere else in whatever code.
For this code to make sense you should at least do one of the following:
class Myclass { int i; }; //I can furthermore instantiate variables of Myclass
class { int i; } myvar; //This in fact creates a myvar object
typedef class { int i; } MyType; //I can funthermore instantiate variables of MyType
By saying just class{ int i; }; you are saying to the compiler:
keep an int and name it i,
wrap it into a class I'll never call and...
forget it! (};)
If you remove that declaration from your program, nothing will change.
class { int i; }; is not a valid declaration because it is a simple-declaration without an init-declarator-list but it doesn't introduce (or re-declare) a class name.
ISO/IEC 14882:2011 7 [dcl.dcl] / 3:
In a simple-declaration, the optional init-declarator-list can be omitted only when declaring a class (Clause 9) or enumeration (7.2), that is, when the decl-specifier-seq contains either a class-specifier, an elaboratedtype-specifier with a class-key (9.1), or an enum-specifier. In these cases and whenever a class-specifier or enum-specifier is present in the decl-specifier-seq, the identifiers in these specifiers are among the names being declared by the declaration (as class-names, enum-names, or enumerators, depending on the syntax). In such cases, and except for the declaration of an unnamed bit-field (9.6), the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration.
The error message from GCC explains it quite succinctly:
$ cat > a.cc
class { int i; };
$ g++ -Wall -std=c++98 a.cc
a.cc:1: error: abstract declarator ‘<anonymous class>’ used as declaration
class { int i; } is an abstract-declarator (Standard, §8) but not a valid declaration (§7). That's the rule that #JohannesSchaub-litb referenced: for a valid declaration, you need something to be declared, e.g. a class name or variable name.
You are breaking the the [basic.scope.pdecl]/6, which says :
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 ]
you are not creating a variable of an anonymous type
you are not create a type
There is another example (in [basic.def]/2) from the standard that proves your example is not standard compliant :
struct S { int a; int b; }; // defines S, S::a, and S::b
struct X { // defines X
int x; // defines non-static data member x
static int y; // declares static data member y
X(): x(0) { } // defines a constructor of X
};
int X::y = 1; // defines X::y
enum { up, down }; // defines up and down
namespace N { int d; } // defines N and N::d
namespace N1 = N; // defines N1
X anX; // defines anX
Your example doesn't define anything (except an anonymous struct, who's fields can not be accessed).
Note an exception about the enum, because this case introduces two values to use.
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
According to ($ 3.4.4) A typedef name followed by a class-key is invalid. But I'm not sure about which scope? For example: In the following, compiler doesn't complain if elaborated specifier was used in a block such as inside a function.
typedef class { /* ... */ } S;
// invalid
class S;
// ok
void foo() {
class S;
}
Is it valid to declare a class inside a local scope with typedef-name, Why?
7.1.3 paragraph 3 tells :
In a given scope, a typedef specifier shall not be used to redefine
the name of any type declared in thascope to refer to a different
type.
[Example:
class complex { /* ... */ };
typedef int complex; //
error: redefinition
Then it goes :
—end example] Similarly, in a given scope, a class or enumeration
shall not be declared with the same name as a typedef-name that is
declared in that scope and refers to a type other than the class or
enumera- tion itself. [Example:
typedef int complex;
class complex { /* ... */ };
// error: redefinition
This is your example.
The problem is that you declared the class with no name, but with an alias (the typedef). Later, you used the same name to declare without defining another class (I know that was not the intention, but that's what the compiler understood) and its name clashed with the typedef. When you did the same thing inside foo(), that was a separated scope and so was acceptable. But notice that the 'class S' inside foo() is NOT the same type as that declared in the first line.
Outside the function, you cannot declare a class with the same name as a typedef in the same namespace.
Inside the function, you are declaring a new class, scoped inside the function. It is not the same as the anonymous class declared in the surrounding namespace. Within the function, this hides the declaration of the typedef.
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.