The title is already the question.
More details: the standard enacts:
If the enum-key is followed by a nested-name-specifier, the enum-specifier shall refer to an enumeration that
was previously declared directly in the class or namespace to which the nested-name-specifier refers (i.e.,
neither inherited nor introduced by a using-declaration), and the enum-specifier shall appear in a namespace
enclosing the previous declaration.
at 7.2, paragraph 4.
For example, this prohibits to forward-declare an enum defined inside a class:
struct S{
enum foo{A, B};
};
now, S can be forward-declared, while S::foo not.
Question is about why. Is there a situation in which this rule can be a benefit? Why is it needed? Or, if you prefer: if the standard had not this rule, is there a situation in which the compiler would have a problem? Which one(s)?
At least, if forward-declare an enum was allowed, it would have created problems with template specializations like the one in the following example:
// somewhere in a .cpp
template<typename>
struct S;
enum S<int>::E;
// somewhere in a galaxy far, far away
template<typename>
struct S { enum class E {}; };
template<>
struct S<int> {};
How could the compiler know (and verify) that enum S<int>::E; is actually defined?
That said, even when you deal with namespaces you cannot do this:
struct X::A;
namespace X { struct A {}; }
But you can do this:
namespace X { struct A; }
namespace X { struct A {}; }
Using classes would result in a code like the following one:
struct A { enum E; };
struct A { enum E {} };
Anyway, this would violate the odr and it is not allowed.
Now, I'll try to give you my impression about the why.
If a forward-declaration of that type was allowed, you would have been allowed to give a partial definition of the containing class.
In other terms, consider this: enum S::E. This states firmly that S contains the enum class E, thus you are giving a clue about the definition of S. To speak not in standardese (that is far from being my natural language) you are partially defining S, thus the compiler should know that S has its definition somewhere plus it must have a definition for E too (either as part of the primary definition or as an out-of-class definition).
This would break the odr rules when the actual definition comes into view, so it cannot be allowed in any case, but as an exception of the basics rules of the language.
Moreover, this is a great source of headaches.
My two cents.
A scoped enum is declared with enum class (or enum struct, not with struct { enum …. That would be an unscoped enumeration, in the scope of a class.
struct S {
enum foo {A, B}; // Not a scoped enumeration.
};
A scoped enumeration can be forward-declared inside a class and defined outside:
struct S {
enum class foo;
};
enum class S::foo { A, B };
However, you cannot declare a class member outside the class, unless it was already declared and you're defining it. Allowing member declarations outside would go against the principle that a class { } definition declares all the class members, that C++ classes are "closed."
Put another way, the rules for declaring and defining member scoped enumerations are essentially the same as for member functions or member classes.
enum Enumeration; enum is not allowed to forward declare in header file.
enum class Enumeration; enum class is allowed to forward declare in header file.
Related
Referring to this code:
namespace Y {
char f() { return y; } // error: y was not declared in this scope
char y;
}
struct X {
char f() { return x; } // OK
char x;
};
As per basic.scope.namespace#1:
The declarative region of a namespace-definition is its
namespace-body. Entities declared in a namespace-body are said to be
members of the namespace, and names introduced by these declarations
into the declarative region of the namespace are said to be member
names of the namespace. Its potential scope includes its namespace from the name's point of declaration onwards.
QUESTIONS
Is it correct to say that the error was due to the fact that Y::y is not yet declared in Y::f()?
I can't figure why there isn't a "reodering" for namespace members during declaration while for struct there is. What could be the reason for disallowing such behavior? Does the standard says something similar?
Is it correct to say that the error was due to the fact that Y::y is not yet declared in Y::f()?
Yes.
I can't figure why there isn't a "reodering" for namespace members during declaration while for struct there is. What could be the reason for disallowing such behavior? Does the standard says something similar?
The biggest hindrance for namespaces as opposed to classes, is that they are never "closed". You can always add members to a namespace by re-opening it in another translation unit and declaring more stuff in it. A class declaration however, for all its members, must appear entirely in a single translation unit. And there is no adding to it later.
[class.mem]/1
The member-specification in a class definition declares the full set
of members of the class; no member can be added elsewhere.
The problem for namespaces is intractable. But classes only require a little more, quite localized, work.
It is therefore much easier, both to a language designer and a compiler writer, to demand namespace declarations appear before they are used.
You should also note that you can only use other members of the class in only a specific set of places inside the definition.
[class.mem]/6
A class is considered a completely-defined object type ([basic.types])
(or complete type) at the closing } of the class-specifier. Within the
class member-specification, the class is regarded as complete within
function bodies, default arguments, noexcept-specifiers, and default
member initializers (including such things in nested classes).
Otherwise it is regarded as incomplete within its own class
member-specification.
You are mistaken thinking that for structures there is "reordering".
Consider the following structure definition.
#include <iostream>
struct A
{
char s[N];
enum { N = 10 };
};
int main()
{
return 0;
}
The compiler will issue an error because the name N is not yet declared when it is used in the array declaration. So neither "reordering" exists in the class scope. As for member functions then names used within a member function are at first searched in the class scope.
Moreover a name declared in a namespace can hide the same name declared in the outer namespace. As result the "reordering" breaks scopes of variables.
Not sure where to ask (feel free to close this if it is an inappropriate question) but I have not found anything on this specifically in C++17 proposals, neither this or this mentions it when dealing with the nested namespace addition to C++.
So currently this is the only option:
class A
{
public:
class B; //forward-declared INSIDE class/namespace
};
class A::B //defined outside
{
};
Will this be possible in C++17?
class A::B; //forward declared NESTED outside of parent class/namespace
class C
{
A::B *b;
};
and then either this (1) (as seems to be the proposal of nested namepsace definitions)
class A::B //definition of A::B without defining A
{
};
or this (2)
class A
{
public:
class A::B
{
};
};
or this [3]
class A
{
public:
class B;
};
class A::B
{
};
I suspect the definition of A::B without defining A first might not work though (although the proposal seems to allow it).
There's a proposal on the issue titled Forward declarations of nested classes P0289R0. However as you can see from the last Trip Report: C++ Standards Meeting in Jacksonville, February 2016, this proposal was pendent to proposals for which further work is encouraged. I'm quoting the verdict of the committee (Emphasis Mine):
This would allow things like X::A* to appear in a header without
requiring a definition for X to also appear in the header
(forward-declarations of X and X::A will be sufficient). EWG found the
use case compelling, because currently a lot of class definitions to
appear in headers only because interfaces defined in the header use
pointers or references to nested classes of the type. Several details
still need to be worked out. (For example, what happens if a
definition of X does not appear in any other translation unit (TU)?
What happens if a definition of X appears in another TU, but does not
define a nested class A? What happens if it does define a nested class
A, but it’s private? The answer to some or all of these may have to be
“ill-formed, no diagnostic required”, because diagnosing errors of
this sort would require significant linker support.)
IMHO lacking the ability to do forward deceleration of classes is a major hole in C++ language definition, which results in people using void* where a forward reference would be more safe.
Here is a workaround using namespaces:
Flatten the class structure you need to pre-declare
Use namespaces to separate the code
Do the forward deceleration using the namespaces
namespace ns1 {
namespace ns2 {
typedef class C * cptr_t; // declare both class and a pointer type
}}
ns1::ns2::C *cp; // use forward deceleration of the class
ns1::ns2::cptr_t cp; // use the typedef
This workaround does not solve the problem properly but may help in some situations.
I'm writing a class (in a header file) that requires the following method:
static const foo::bar::clz* getSomething(void);
As I don't want to pollute the header file with an #include I choose to forward declare clz:
namespace foo
{
namespace bar
{
class clz;
}
}
But that's rather clumsy too. Is there a cuter way of doing this in C++11? Something along the lines of
class foo::bar::clz;
It would be nicer still if you didn't have to anticipate whether or not it's implemented as a class, struct or union. Have the C++11 grammarians covered that one too? (typename auto could be a candidate but I'm not an expert in C++ grammar).
The nested namespaces can't be avoided.
As for class vs. struct vs. union, partially: class and struct can be used interchangeably. If it's a union, it must be declared as such.
I'm looking at this resource:
http://www.cplusplus.com/reference/vector/vector/
For example, the iterator member type on class vector.
Would a "member type" simply be implemented as a typedef or something similar in the vector class? It is not clear to me what "member type" actually means, and I've looked at a couple C++ textbooks, they don't even mention this phrase at all.
The C++ Standard does not use this phrase either. Instead, it would call it a nested type name (§9.9).
There are four ways to get one:
class C
{
public:
typedef int int_type; // as a nested typedef-name
using float_type = float; // C++11: typedef-name declared using 'using'
class inner_type { /*...*/ }; // as a nested class or struct
enum enum_type { one, two, three }; // nested enum (or 'enum class' in C++11)
};
Nested type names are defined in class scope, and in order to refer to them from outside that scope, name qualification is required:
int_type a1; // error, 'int_type' not known
C::int_type a2; // OK
C::enum_type a3 = C::one; // OK
Member type simply stands for a type that is a member(of that class). It could be a typedef as you say (in the case of vectorit is likely to be T*) or it could be nested class (or struct).
Member type may refer to 'nested class' or 'nested structure'.
It means class inside another class. If you want to refer text books then search for 'nested classes'.
I did a mistake in my program, I wrote
struct vector<int> v;
instead of
vector<int> v;
but it seems that the compiler doesn't care: http://codepad.org/TCPb8p2u
Why does it work? Is there some differencies with or without struct?
If you write class, it would also work.
class vector<int> v;
See this: http://www.ideone.com/EoJxk
This is actually old C style. The C++ Standard calls it elaborated-type-specifier in section §3.4.4.
The keyword struct (or class, enum) is sometimes used to remove ambiguities, or to make hidden names visible to the compiler. Consider the following example from the Standard itself (from section §9.1/2). Please notice that there exists a struct with name stat and with exactly same also exists a function:
struct stat {
// ...
};
stat gstat; // use plain stat to define variable
int stat(struct stat*); // redeclare stat as function
void f()
{
struct stat* ps; // struct prefix needed to name struct stat
// ...
stat(ps); //call stat()
// ...
}
§9.1/2 says,
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). If a
class name is declared in a scope
where an object, function, or
enumerator of the same name is also
declared, then when both declarations
are in scope, the class can be
referred to only using an
elaborated-type-specifier (3.4.4).
This is a feature of C++ that's used to resolve ambiguity between a variable and type with the same name. I believe they're called 'elaborate type specifiers.'
You can use a keyword to tell the compiler exactly what you mean, when there would normally be ambiguity.
Take this for example:
int x = 0;
class x { };
// Compiler error! Am I refering to the variable or class x?
x y;
// This is okay, I'm telling the compiler which x I'm referring to.
class x y;
This can also be used to specify enums and unions, not just structs and classes, though you can only have one user-defined type with the same name.
The others have already explained it that what you uses is called elaborated type specifier, which can be used for name unhiding and ambiguity resolution.
However, what also works here is one curious feature of C++ language, which states that class-key (i.e. class or struct keyword) used in class declaration is not required to agree with the class-key used in the elaborated type specifier. E.g. you can declare your class with class keyword and then later refer to it with struct keyword (and vice versa). There's no error in it
class S {};
int main() {
struct S s; // OK, 's' has type `S` (i.e. `class S`)
}
Standard std::vector template class is declared with keyword class, but there's no error in referring to it as struct std::vector, which is what you did in your example.
And no, it makes no difference whether in your declaration of v you use class, struct or nothing at all.