Nested enum and nested class have different behavior - c++

Consider these two examples:
struct X
{
class E { static const int z = 16 };
static const int b = X::z; // X has no member z
};
struct Y
{
enum E { z = 16 };
static const int b = Y::z; // OK
};
Is there a section of the standard that explains this behavior?

Yes, there are such sections in the C++ Standard.
The first one is
9.9 Nested type names
1 Type names obey exactly the same scope rules as other names. In
particular, type names defined within a class definition cannot be
used outside their class without qualification.
It would be more precisely to cite the following quote
2 The name of a class member shall only be used as follows: — in the
scope of its class (as described above) or a class derived (Clause 10)
from its class, — after the . operator applied to an expression of the
type of its class (5.2.5) or a class derived from its class, — after
the -> operator applied to a pointer to an object of its class (5.2.5)
or a class derived from its class, — after the :: scope resolution
operator (5.1) applied to the name of its class or a class derived
from its class.
and the second one is
11.7 Nested classes
1 A nested class is a member and as such has the same access rights as
any other member. The members of an enclosing class have no special
access to members of a nested class; the usual access rules (Clause 11)
shall be obeyed.
In this definition if do not take into account a typo (the absence of a semicolon after the definition of z)
struct X
{
class E { static const int z = 16 };
static const int b = X::z; // X has no member z
};
You are trying to access z 1) without qualification and 2) that has private access control.
The correct definition could look as
struct X
{
class E { public: static const int z = 16; };
static const int b = E::z;
};
As for the enumeration then enumerators of an unscopped enumeration are members of the class where the enumeration is defined.
9.2 Class members
1 The member-specification in a class definition declares the full set
of members of the class; no member can be added elsewhere. Members of
a class are data members, member functions (9.3), nested types, and
enumerators.

[C++11: 7.2/10]: Each enum-name and each unscoped enumerator is declared in the scope that immediately contains the enum-specifier. [..]
No such rule exists for classes.

You asked for citations from the Standard, and you've got the right ones, but it seems they don't help:
How this implies that enum members can be accessed from the outer declarative region? – St.Antario
+1 to St.Antario. I don't understand it to. – zavhoz
So I'll try the investigative approach. If we fix the irrelevant bugs in your
example struct X:
Missing ; after 16.
E::z is priviate.
We have:
struct X
{
struct E { static const int z = 16; };
static const int b = X::z;
};
Compiling your code thus fixed, clang 3.4 complains:
error: no member named 'z' in 'X'; did you mean 'E::z'?
static const int b = X::z;
^~~~
E::z
(gcc 4.9.0 and VC++ 2013 gives errors at the same line, but with less helpful
diagnostics)
No such complaint about Y::z, however. The lesson seems to be:
A class definition does not declare the class members in the enclosing
scope.
A plain old enum definition does declare the enumerators in the
enclosing scope.
I say plain old enum here, because as of C++11 we have also got a new fancier
kind of enum, but Y::E is a plain old one.
If that's the lesson, it does not appear to have anything in particular
to do with nesting.
Vlad from Moscow has cited you a paragraph of the Standard from 11.7 Nested classes
from which he presumably wants you to gather that nesting is irrelevant to your
puzzle. But the fact that the compiler barfs at the qualified name X::z
and does not barf at the qualified name Y::z makes it look as if the
nesting of struct E in struct X has some significance that the nesting of
enum E in struct Y does not.
Recall that for any name N that is declared in a scope S, the
qualification S::N is redundant within the scope of S, just as the
qualification ::N is redundant at global scope for a global name N.
If there is something declared z in the scope of X[Y], then within the scope of
X[Y] you can refer to it as just z. So let's delete the superfluous
qualifications from the example code:
struct X
{
struct E { static const int z = 16; };
static const int b = z;
};
struct Y
{
enum E { z = 16 };
static const int b = z;
};
and see what clang makes of it now:
error: use of undeclared identifier 'z'; did you mean 'E::z'?
static const int b = z;
^
E::z
It has just the same problem in X; still has no problem in Y. But since
we've got rid of the superfluous qualification, X isn't mentioned at all.
And Y has never been mentioned. So maybe now it no longer looks as if
nesting has something to do with the puzzle.
So next let's go ahead and get rid of X and Y. Here's the program:
struct E { static const int z = 16; };
static const int b = z;
enum E1 { z = 16 };
static const int b = z;
int main()
{
return 0;
}
And clang says:
error: use of undeclared identifier 'z'; did you mean 'E::z'?
static const int b = z;
^
E::z
No change at all.
So nesting has, indeed, nothing to do with the puzzle. The fact of the matter is
that struct|class members are declared in the struct|class scope, not
in the enclosing scope, and the enumerators of a plain old enum are declared in the
enclosing scope. Lightness Races in Orbit has given you the Standard reference
for the latter fact.
Does this still surprise you? It can hardly suprise you that:
struct E { static const int z = 16; };
static const int b = z;
does not declare z in the same scope as b. So possibly it suprises you that:
enum E1 { z = 16 };
static const int b = z;
does declare z in the same scope as b? Well, that is how plain old enums
have always been, since C++ inherited them and C. That is largely what makes
them plain old enums.
This scope-hoisting trait of plain old enums is the reason why, in C++11
jargon, plain old enums are called unscoped enums. And this trait, as
well as the readiness of unscoped enumerators to decay to int, has
made unscoped enums long a source of discomfort in C++.
Hence in C++11 we now have a more strictly behaved alternative, called a scoped enum or
enum class which goes something like this:
enum struct SE : short {
z = 16
};
//const int b = z; <- Undeclared identifier
//const short b = SE::z; <- No implicit conversion
SE b = SE::z; //OK
const int c = static_cast<int>(SE::z); //OK
It looks as if scoped enums would not surprise you as to the scoping
of their enumerators but would suprise you by resisting implicit
integral conversions.
Further reading.

Related

What does this passage from cppreference.com (Default arguments) mean?

From cppreference page on default arguments:
Non-static class members are not allowed in default arguments (even if they are not evaluated), except when used to form a pointer-to-member or in a member access expression:
int b;
class X
{
int a;
int mem1(int i = a); // error: non-static member cannot be used
int mem2(int i = b); // OK: lookup finds X::b, the static member
static int b;
};
I can't understand "except when used to form a pointer-to-member or in a member access expression". And the example does not give the relevant code.
The first part means that you are allowed to form a pointer-to-member to one of the non-static members, e.g.:
class X
{
int a, b;
int mem1(int X::* i = &X::a);
//...
};
A pointer-to-member is a rather obscure part of the language. You have maybe seen member function pointers, but you are also allowed to form such member pointers to data members as above.
The member pointer doesn't refer to the member of the current instance of the class, but needs to be combined with the .* or ->* operators to give the corresponding member of an instance, e.g.:
int X::mem1(int X::* i = &X::a) {
// same as `return a;` if called with default argument
// but if called with `mem1(&X::b)` same as `return b;`
return this->*i;
}
The second part probably just means that it is ok to refer to the member of another instance of the class via the usual member access expressions (using . or ->). The exception doesn't allow referring to the current instance's members since this is also not allowed in the default argument:
class X
{
int a;
static X x;
int mem1(int i = x.a); // ok, `.` is member access
int mem2(int i = this->a); // not ok because of `this`, but `->` is member access
};
Forming a pointer-to-member:
int mem3(int X::*pointer_to_member = &X::a);
Used in a member-access expression:
X global;
int mem4(int i = global.a);
Demo

The code below doesn't compile. Maybe because GCC with std=c++2a is still not completely up to date with the most recent draft

[class.mem]/6:
A complete-class context of a class is a
(6.1) function body, (6.2) default argument, (6.3)
noexcept-specifier ([except.spec]), (6.4) contract condition, or (6.5) default member initializer
within the member-specification of the class. [ Note: A
complete-class context of a nested class is also a complete-class
context of any enclosing class, if the nested class is defined within
the member-specification of the enclosing class. — end note ]
This paragraph was introduced in the draft with pull-request #2231.
As far as I can understand, the following code should compile, according to the Note above. But it doesn't. I'm assuming that the GCC compiler is still not up to date with the most recent draft. Am I correct, or is it the case that my understanding about this note is incorrect?
struct A {
int i = 1;
struct B {
int j = 2;
int f() {
return i + j;
}
};
};
Which fails with:
source>: In member function 'int A::B::f()':
<source>:6:20: error: invalid use of non-static data member 'A::i'
6 | return i + j;
| ^
<source>:2:9: note: declared here
2 | int i = 1;
| ^
I think the confusion stems here from what the point of complete-class context is and how is it intended to be used.
Importantly, name lookup will find i there. So I can write this:
struct A {
struct B {
int j = 2;
int f() {
using T = decltype(i); // ok, even though 'i' declared later lexically
return T{} + j; // ok
}
};
int i = 1;
};
As well as:
struct A {
struct B {
int j = 2;
int f() {
return i + j; // ok, even though 'i' declared later lexically
}
};
static const int i = 1;
};
Indeed, this was ok all the way back in C++11.
But i is still a non-static member, so you can only access it from the context of an object of type A. Within the body of a member function of B, we don't implicitly have an A object. So such a free use of i is still ill-formed.
That said, this:
I'm assuming that the GCC compiler is still not up to date with the most recent draft.
Is certainly true, and will remain true for quite some time.
Then what exactly is that note saying?
Names declared within the class A are in scope within the complete-class context even if before the point of declaration of the name.
The note means that the complete-class context of the enclosing class (noting the pre-condition) extends to the complete-class context of the nested class.
Therefore following is well-formed:
struct A {
struct B{
void foo() {
// names are in scope despite being before point of declaration
I i;
var;
}
};
using I = int;
static int var;
};
You cannot access a non-static data member in a (non-static) member function of another class just like you cannot access non-static members in a static member function of the same class. Complete-class context does not change that.

Why is address of non-static member not allowed as template non-type parameter?

template <int* ip> struct test {};
struct q {
static int a;
int b;
constexpr q(int b_) : b(b_) {}
};
int i;
constexpr q q0(2);
int main()
{
constexpr test<&i> t1; // Works fine
constexpr test<&q::a> t2; // Works
constexpr test<&q0.b> t3; // Does not work; address of non-static member?
return 0;
}
The declaration of t3 in the above piece of code fails despite the template argument &q0.b being known during compile time. Some googling revealed that this is disallowed by the standard (section 14.3.2):
[Note: Addresses of array elements and names or addresses of non-static class members are not acceptable template-arguments.
X<&s.m> x4; // error: address of non-static membe
So why exactly is this explicitly disallowed by the standard despite the addresses of non-static members of global variables being unique as well as known during compile-time?
First, to use pointers/references to subobjects, you'd need to be able to mangle them. That's a pretty big undertaking.
Second, and probably more importantly, from N4198:
The restriction that the constant expression must name a complete
object is retained to avoid aliasing problems with pointers to
subobjects:
struct A { int x, y; } a;
template<int*> struct Z;
using B = Z<&a.x + 1>;
using C = Z<&a.y>;
// Are B and C the same type?
To quote Richard Smith,
The answer "yes" is problematic because there are things you can do
with a pointer to [a.y] that would have undefined behavior if
performed on a pointer past the end of [a.x] The answer "no" is
problematic because they (in typical implementations) represent the
same address.
Replace your main with this piece of code
int main(void)
{
constexpr static int bb = 5;
constexpr test<&bb> t;
return 0;
}
and you will receive the error bb cannot be used as a template argument because it has no linkage ( not to be mistaken with linker related staff ) .
Class data members cannot be accessed unless they are referenced through the object, and cannot be taken into account during template instantiation, since data members do not have linkage, i.e. they are not defined symbols and therefore cannot be used as template arguments.
That being said , doing a readelf you can verify this:
48: 00000000004006ac 4 OBJECT LOCAL DEFAULT 14 q0
68: 000000000060097c 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
69: 0000000000600978 4 OBJECT GLOBAL DEFAULT 23 q::a
but there is no q0.b defined . The alternative would be to name (mangle) none static class members which would strip dynamic capabilities of the language.

Scope of members in class

In the following example, will the size of array v guaranteed to be 2 or 3?
static const int i = 3;
class X {
char v[i];
static const int i = 2;
};
From the standard,
3.3.6/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
I think this means 'i' shall be 2 and what does the re-evaluation thing really means here?
The correct behavior is that it should cause an error because re-evaluation would change the meaning:
Example from section 3.3.6:
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, for constructor functions (12.1), the ctor-initializer (12.6.2)) and any portion of the declarator part of such definitions which follows the identifier, including a parameter-declaration-clause and any default arguments (8.3.6). [Example:
The example is similar to yours (using enum instead of a static const int):
typedef int c;
enum { i = 1 };
class X {
char v[i]; // error: i refers to ::i
// but when reevaluated is X::i
int f() { return sizeof(c); } // OK X::c
char c;
enum { i = 2 };
};
At the time v[i] is encountered, the compiler only knows about enum { i = 1 }; (or static const int i = 3;, but when the full class declaration is known, char v[i] would be different because i would be re-evaluated to 2.
Array size should be 3 in this case. If you look in your code line by line. Compliler know nothing about X::i when construct array. If you change lines inside class when size of array become 2 and second i will hide first.

Defining static const integer members in class definition

My understanding is that C++ allows static const members to be defined inside a class so long as it's an integer type.
Why, then, does the following code give me a linker error?
#include <algorithm>
#include <iostream>
class test
{
public:
static const int N = 10;
};
int main()
{
std::cout << test::N << "\n";
std::min(9, test::N);
}
The error I get is:
test.cpp:(.text+0x130): undefined reference to `test::N'
collect2: ld returned 1 exit status
Interestingly, if I comment out the call to std::min, the code compiles and links just fine (even though test::N is also referenced on the previous line).
Any idea as to what's going on?
My compiler is gcc 4.4 on Linux.
My understanding is that C++ allows static const members to be defined inside a class so long as it's an integer type.
You are sort of correct. You are allowed to initialize static const integrals in the class declaration but that is not a definition.
Interestingly, if I comment out the call to std::min, the code compiles and links just fine (even though test::N is also referenced on the previous line).
Any idea as to what's going on?
std::min takes its parameters by const reference. If it took them by value you'd not have this problem but since you need a reference you also need a definition.
Here's chapter/verse:
9.4.2/4 - If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.
See Chu's answer for a possible workaround.
Bjarne Stroustrup's example in his C++ FAQ suggests you are correct, and only need a definition if you take the address.
class AE {
// ...
public:
static const int c6 = 7;
static const int c7 = 31;
};
const int AE::c7; // definition
int f()
{
const int* p1 = &AE::c6; // error: c6 not an lvalue
const int* p2 = &AE::c7; // ok
// ...
}
He says "You can take the address of a static member if (and only if) it has an out-of-class definition". Which suggests it would work otherwise. Maybe your min function invokes addresses somehow behind the scenes.
Another way to do this, for integer types anyway, is to define constants as enums in the class:
class test
{
public:
enum { N = 10 };
};
Not just int's. But you can't define the value in the class declaration. If you have:
class classname
{
public:
static int const N;
}
in the .h file then you must have:
int const classname::N = 10;
in the .cpp file.
Here's another way to work around the problem:
std::min(9, int(test::N));
(I think Crazy Eddie's answer correctly describes why the problem exists.)
As of C++11 you can use:
static constexpr int N = 10;
This theoretically still requires you to define the constant in a .cpp file, but as long as you don't take the address of N it is very unlikely that any compiler implementation will produce an error ;).
C++ allows static const members to be defined inside a class
Nope, 3.1 §2 says:
A declaration is a definition unless it declares a function without specifying the function's body (8.4), it contains the extern specifier (7.1.1) or a linkage-specification (7.5) and neither an initializer nor a functionbody, it declares a static data member in a class definition (9.4), it is a class name declaration (9.1), it is an opaque-enum-declaration (7.2), or it is a typedef declaration (7.1.3), a using-declaration (7.3.3), or a using-directive (7.3.4).