class A {
public: enum class { HELLO, WORLD };
};
Having known that, inside a class, declaring a simple enum (rather than enum class) is a better idea, because it's already typed with the class identification. But still above statement is a valid C++0x signature. Now how to access an unnamed enum class outside ?
int i = A::HELLO; // error: ‘HELLO’ is not a member of ‘A’
Actually, that is not valid. The C++0x FDIS says (9.2p1)
Except when used to declare friends (11.3) or to introduce the name of a member of a base class into a derived class (7.3.3), member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class.
In your case, no enumerator name is introduced into the class' scope and no enumeration name is introduced either. So, no member name at all is introduced by that member-declaration.
EDIT: And actually, there's a more direct prohibition of the enumeration declaration. 7.2p2:
The optional identifier shall not be omitted in the declaration of a scoped enumeration.
Related
This question already has answers here:
Can typedef names be used to declare or define constructors?
(2 answers)
Using class alias for its constructor definition [duplicate]
(1 answer)
Closed 3 months ago.
Take this class:
class Foo {
public:
using MyType = Foo;
MyType* m = nullptr; //ok
MyType* functor() { //ok
return nullptr;
}
MyType() = default; //error
~MyType() = default; //error
};
Why can you use alias names for members, but not for constructors or destructors?
Since this is tagged language-lawyer, lets see what the standard has to say.
From [class.ctor.gen], emphasis added:
A declarator declares a constructor if it is a function declarator
([dcl.fct]) of the form ptr-declarator ( parameter-declaration-clause
) noexcept-specifieropt attribute-specifier-seqopt where the
ptr-declarator consists solely of an id-expression, an optional
attribute-specifier-seq, and optional surrounding parentheses, and the
id-expression has one of the following forms:
(1.1) in a friend declaration ([class.friend]), the id-expression is a qualified-id that names a constructor ([class.qual]);
(1.2) otherwise, in a member-declaration that belongs to the member-specification of a class or class template, the id-expression
is the injected-class-name ([class.pre]) of the immediately-enclosing
entity;
(1.3) otherwise, the id-expression is a qualified-id whose unqualified-id is the injected-class-name of its lookup context.
For the subject of why a name alias is not valid to declare constructor, especially (1.2) is of relevance. It states that the id-expression is the injected-class-name [..]. The latter is defined in [class.pre]:
A class is a type. Its name becomes a class-name ([class.name]) within its scope.
[..]
The class-name is also bound in the scope of the class (template) itself; this is known as the injected-class-name.
For purposes of access checking, the injected-class-name is treated as if it were a public member name. [..]
This states very plainly, that the injected-class-name is exactly the name used to declare the class in the first place. There is no allowance in the wording for aliases.
This follows the same mechanism that you see in a class template:
template <typename T> class C { C foo(); };
Now, there is no class named C, C is a template, so you would normally expect to have to say C<T> foo();. But as stated in [class.pre], a class-name is introduced in the scope of the class definition which refers to the actual type. So, even if there is no class C anywhere, the class-name C can be used as a shorthand for C<T> and in the case of constructors and destructors, is the only way to refer to it because their declaration does not allow you to use any type-id, but only class-name.
m is a pointer to an instance of a type, which is aliased. OK.
functor() returns a pointer to an instance of a type, which is aliased. OK.
But a constructor/destructor is not itself a type, so you can't use a type alias for them. They must be named after the type they belong to. That is just the way the syntax works
How can I create an alias for a lengthy class name inside the definition of this class? I don't want it to be accessible outside the class definition. I can't get it to work with either typedef or using.
It's a class template in my case.
template<typename T>
class detailed_class_name{
typedef detailed_class_name<T> foo; // either one
using foo = detailed_class_name<T>; // of these
public:
foo(); // Error: ISO C++ forbids declaration of 'foo' with no type.
};
Any ideas?
You cannot. A constructor's id-expression, i.e. the name of the constructor in a class definition is according to [class.ctor]p1 (emphasis mine):
in a member-declaration that belongs to the member-specification of a class but is not a friend declaration, the id-expression is the injected-class-name of the immediately-enclosing class;
And what is an injected-class-name? According to [class]p2
A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. [...]
And so the injected-class-name is the name of the class itself, and cannot be an alias.
This is further reinforced by this sentence in [class.ctor]p1:
The class-name shall not be a typedef-name.
Consider renaming the class if you don't even want to write a constructor because the name is too convoluted.
In the c++ standard it is specified that within the class member-specification (class body), the class can be considered completely-defined, but not for static data member initializer [class.mem]:
A class is considered a completely-defined object type (6.9) (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.
EDIT: This is a citation from N4687, wording has changed but I do not believe the meaning changed.
I was expecting such code to compile:
struct enum_like
{
static constexpr enum_like enum_member{};
};
Why such a definition disallowed by the C++ standard?
I believe compilers could proceed this way:
read member declaration, not definition until class definition closing brace. (Now the compiler has a completely defined class)
Analyse static data-member initializer (This way compilers have the constant definition of constexpr members)
Analyse other member definitions.
And then resolve recursions for static member intializer as is specified in [decl.init] for non static members!
This rule forbids problematic things like:
struct A {
static constexpr std::size_t N = sizeof(A);
char buffer[N+2];
};
According to the C++ standard,
9.2 [class.mem]:
A class is considered a completely-defined object type (3.9) (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, using-declarations introducing
inheriting constructors (12.9), exception-specifications, and
brace-or-equal-initializers for non-static data members (including
such things in nested classes). Otherwise it is regarded as incomplete
within its own class member-specification
So, the code below should compile, and indeed it does
struct Foo{
Foo()
{
Bar bar; // Bar is fully visible here, even though it's defined later
}
//void f(Bar){} // But NOT VISIBLE if used as a function parameter
struct Bar{};
};
int main()
{
Foo foo;
}
Live on Coliru
However, if I uncomment the line that defines the member function void Foo::f(Bar), then the code fails to compile with the error
error: 'Bar' has not been declared
Reading again the standard it indeed seems that function parameters are not considered as places where the class is regarded as complete. However, it does not make any sense at all. Can you shed some light why I cannot use Bar in a function parameter (but otherwise can fully use it inside a function without any issues whatsoever) before its full definition?
In all the cases listed in 9.2 [class.mem] knowing the type can be deferred until the class is fully defined. We can see this rationale listed in defect report 643: Use of decltype in a class member-specification which says:
In the other cases where a class type is considered complete within the definition of the class, it is possible to defer handling the construct until the end of the definition. That is not possible for types, as the type may be needed immediately in subsequent declarations.
As T.C. points out there is also issues of lookup involved as defect report 325: When are default arguments parsed? and defect report 1352 deal with. The later one also mentions the same technique of being able to defer parsing till the class is complete:
The rules regarding class scope and when the class is considered to be complete (normally implemented by deferred parsing of portions of class member declarations) are inconsistent and need to be clarified.
From the 03 standard, 3.4.1/8 (Unqualified name lookup):
A name used in the definition of a member function (9.3) of class X following the function’s declarator-id29)
shall be declared in one of the following ways:
— before its use in the block in which it is used or in an enclosing block (6.3), or
— shall be a member of class X or be a member of a base class of X (10.2), or
— if X is a nested class of class Y (9.7), shall be a member of Y, or shall be a member of a base class of Y
(this lookup applies in turn to Y’s enclosing classes, starting with the innermost enclosing class),30) or
— if X is a local class (9.8) or is a nested class of a local class, before the definition of class X in a block
enclosing the definition of class X, or
— if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class
or a nested class within a local class of a function that is a member of N, before the member function
definition, in namespace N or in one of N’s enclosing namespaces.
I was going to file a bug against GCC, but then realized that if my interpretation of the Standard is correct, it's a core language defect, not a compiler bug.
When a static data member of array type is defined outside class scope, identifiers in the array bound are looked up in class scope.
§9.4.2 [class.static.data] says "The initializer expression in the definition of a static data member is in the scope of its class (3.3.7)," but doesn't say anything about the declarator itself. It seems that this is the only name lookup context within a declarator.
§8.4.2 [dcl.array] doesn't mention the scope of the array bound, so by default the expression is evaluated in the enclosing scope, which is the namespace.
class X {
static int const size = some_complicated_metafunction<>::value;
static int arr[ size ];
};
// "size" should be qualified as "X::size", which is an access violation.
int X::arr[ size ] = {};
The problem is that if the array bound is not evaluated in class scope, there is no way to access private class members, and some_complicated_metafunction<> would have to be respecified. The array bound needs the same scope as the initializer, for essentially the same reason as the initializer. (Although not quite as strong, since constant expressions can always be recomputed, unlike the address of a private object.)
Am I missing something or is a DR in order?
I think the array bound in a member definition is in class scope. Standard 3.3.7/1:
The following rules describe the scope of names declared in classes.
...
5) The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class (this includes static data member definitions, nested class definitions, member function definitions (including the member function body and any portion of the declarator part of such definitions which follows the declarator-id, including a parameter-declaration-clause and any default arguments (8.3.6). ...
Here the declarator-id is X::arr.