Scope of members in class - c++

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.

Related

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.

Declare the same identifier as const variable and member function

I have wrote the following piece of code
#include <iostream>
const int N = 5;
class X
{
public:
int array[N];
void foo()
{
std::cout << "array size:"<<sizeof(array)/N << std::endl;
}
enum
{
N = 3
};
};
int main()
{
X x;
x.foo();
}
The aforementioned code does not compile with GCC:
<source>:13:8: error: declaration of 'N' [-fpermissive]
N = 3
^
<source>:2:11: error: changes meaning of 'N' from 'const int N' [-fpermissive]
const int N = 5;
^
From my point in compile time the array is defined as an array of five integers and N is defined as 5. How the compiler resolved the name declaration of variables?
Inside the scope of member functions (even those defined inline), the class is considered complete1. As such, using N there must use the member enumerator. And its value must be 3.
But, that is not the case when declaring class member data. At that point (when specifying array), the class is not made to be considered complete. So N can only refer to what was seen previously, which means it must be the global constant.
Clang accepts it, but emits 6 (sizeof(int) * 5 / 3). GCC (8) doesn't, but it isn't really invalid code. It's just error prone. The way to make better defined would be to move the enumerator to before the array is defined
enum { N = 3 };
int array[N];
... or if we don't, then we can use scope resolution to refer to the "correct N"
sizeof(array) / ::N
Rearranging the class definition would be better, since it will not still remain error prone (we can forget to use the qualified ::N).
1: From the latest C++ standard draft
[class.mem]/6
A complete-class context of a class is a
function body ([dcl.fct.def.general]),
default argument ([dcl.fct.default]),
noexcept-specifier,
contract condition ([dcl.attr.contract]), or
default member initializer
within the member-specification of the class.
In the line
int array[N];
N is the global N.
Inside the function foo(), the N is the one defined in the enum.
Inside the definition of foo() the definition of the class is used to resolve names. However, in the declaration of a member variable, only the declarations up to that line is used to resolve names.
If you change your class to
class X
{
public:
enum
{
N = 3
};
int array[N];
void foo()
{
std::cout << "array size:"<<sizeof(array)/N << std::endl;
}
};
Then, the N used to define array is the one defined in th enum.
PS
This is useful for understanding the language but, please, never ever use such coding style in real world applications.
The problem comes from the declaration int array[N];.
According to [basic.scope.class]/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 the context of the declaration, N is resolved to refer to ::N, but in the completed scope of X (all members are visible now), N is resolved to refer to the enumerator X::N, so the program is ill-formed; no diagnostic is required.

Variable declarations are definitions? [duplicate]

The meaning of both eludes me.
A declaration introduces an identifier and describes its type, be it a type, object, or function. A declaration is what the compiler needs to accept references to that identifier. These are declarations:
extern int bar;
extern int g(int, int);
double f(int, double); // extern can be omitted for function declarations
class foo; // no extern allowed for type declarations
A definition actually instantiates/implements this identifier. It's what the linker needs in order to link references to those entities. These are definitions corresponding to the above declarations:
int bar;
int g(int lhs, int rhs) {return lhs*rhs;}
double f(int i, double d) {return i+d;}
class foo {};
A definition can be used in the place of a declaration.
An identifier can be declared as often as you want. Thus, the following is legal in C and C++:
double f(int, double);
double f(int, double);
extern double f(int, double); // the same as the two above
extern double f(int, double);
However, it must be defined exactly once. If you forget to define something that's been declared and referenced somewhere, then the linker doesn't know what to link references to and complains about a missing symbols. If you define something more than once, then the linker doesn't know which of the definitions to link references to and complains about duplicated symbols.
Since the debate what is a class declaration vs. a class definition in C++ keeps coming up (in answers and comments to other questions) , I'll paste a quote from the C++ standard here.
At 3.1/2, C++03 says:
A declaration is a definition unless it [...] is a class name declaration [...].
3.1/3 then gives a few examples. Amongst them:
[Example: [...]
struct S { int a; int b; }; // defines S, S::a, and S::b [...]
struct S; // declares S
—end example
To sum it up: The C++ standard considers struct x; to be a declaration and struct x {}; a definition. (In other words, "forward declaration" a misnomer, since there are no other forms of class declarations in C++.)
Thanks to litb (Johannes Schaub) who dug out the actual chapter and verse in one of his answers.
From the C++ standard section 3.1:
A declaration introduces names into a translation unit or redeclares names introduced by previous
declarations. A declaration specifies the interpretation and attributes of these names.
The next paragraph states (emphasis mine) that a declaration is a definition unless...
... it declares a function without specifying the function’s body:
void sqrt(double); // declares sqrt
... it declares a static member within a class definition:
struct X
{
int a; // defines a
static int b; // declares b
};
... it declares a class name:
class Y;
... it contains the extern keyword without an initializer or function body:
extern const int i = 0; // defines i
extern int j; // declares j
extern "C"
{
void foo(); // declares foo
}
... or is a typedef or using statement.
typedef long LONG_32; // declares LONG_32
using namespace std; // declares std
Now for the big reason why it's important to understand the difference between a declaration and definition: the One Definition Rule. From section 3.2.1 of the C++ standard:
No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, or template.
Declaration: "Somewhere, there exists a foo."
Definition: "...and here it is!"
There are interesting edge cases in C++ (some of them in C too). Consider
T t;
That can be a definition or a declaration, depending on what type T is:
typedef void T();
T t; // declaration of function "t"
struct X {
T t; // declaration of function "t".
};
typedef int T;
T t; // definition of object "t".
In C++, when using templates, there is another edge case.
template <typename T>
struct X {
static int member; // declaration
};
template<typename T>
int X<T>::member; // definition
template<>
int X<bool>::member; // declaration!
The last declaration was not a definition. It's the declaration of an explicit specialization of the static member of X<bool>. It tells the compiler: "If it comes to instantiating X<bool>::member, then don't instantiate the definition of the member from the primary template, but use the definition found elsewhere". To make it a definition, you have to supply an initializer
template<>
int X<bool>::member = 1; // definition, belongs into a .cpp file.
Declaration
Declarations tell the compiler that a
program element or name exists. A
declaration introduces one or more
names into a program. Declarations can
occur more than once in a program.
Therefore, classes, structures,
enumerated types, and other
user-defined types can be declared for
each compilation unit.
Definition
Definitions specify what code or data
the name describes. A name must be
declared before it can be used.
From the C99 standard, 6.7(5):
A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that:
for an object, causes storage to be reserved for that object;
for a function, includes the function body;
for an enumeration constant or typedef name, is the (only) declaration of the
identifier.
From the C++ standard, 3.1(2):
A declaration is a definition unless it declares a function without specifying the function's body, it contains the extern specifier or a linkage-specification and neither an initializer nor a function-body, it declares a static data member in a class declaration, it is a class name declaration, or it is a typedef declaration, a using-declaration, or a using-directive.
Then there are some examples.
So interestingly (or not, but I'm slightly surprised by it), typedef int myint; is a definition in C99, but only a declaration in C++.
From wiki.answers.com:
The term declaration means (in C) that you are telling the compiler about type, size and in case of function declaration, type and size of its parameters of any variable, or user defined type or function in your program. No space is reserved in memory for any variable in case of declaration. However compiler knows how much space to reserve in case a variable of this type is created.
for example, following are all declarations:
extern int a;
struct _tagExample { int a; int b; };
int myFunc (int a, int b);
Definition on the other hand means that in additions to all the things that declaration does, space is also reserved in memory. You can say "DEFINITION = DECLARATION + SPACE RESERVATION" following are examples of definition:
int a;
int b = 0;
int myFunc (int a, int b) { return a + b; }
struct _tagExample example;
see Answers.
C++11 Update
Since I don't see an answer pertinent to C++11 here's one.
A declaration is a definition unless it declares a/n:
opaque enum - enum X : int;
template parameter - T in template<typename T> class MyArray;
parameter declaration - x and y in int add(int x, int y);
alias declaration - using IntVector = std::vector<int>;
static assert declaration - static_assert(sizeof(int) == 4, "Yikes!")
attribute declaration (implementation-defined)
empty declaration ;
Additional clauses inherited from C++03 by the above list:
function declaration - add in int add(int x, int y);
extern specifier containing declaration or a linkage specifier - extern int a; or extern "C" { ... };
static data member in a class - x in class C { static int x; };
class/struct declaration - struct Point;
typedef declaration - typedef int Int;
using declaration - using std::cout;
using directive - using namespace NS;
A template-declaration is a declaration. A template-declaration is also a definition if its declaration defines a function, a class, or a static data member.
Examples from the standard which differentiates between declaration and definition that I found helpful in understanding the nuances between them:
// except one all these are definitions
int a; // defines a
extern const int c = 1; // defines c
int f(int x) { return x + a; } // defines f and defines x
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
// all these are declarations
extern int a; // declares a
extern const int c; // declares c
int f(int); // declares f
struct S; // declares S
typedef int Int; // declares Int
extern X anotherX; // declares anotherX
using N::d; // declares N::d
// specific to C++11 - these are not from the standard
enum X : int; // declares X with int as the underlying type
using IntVector = std::vector<int>; // declares IntVector as an alias to std::vector<int>
static_assert(X::y == 1, "Oops!"); // declares a static_assert which can render the program ill-formed or have no effect like an empty declaration, depending on the result of expr
template <class T> class C; // declares template class C
; // declares nothing
Definition :
extern int a; // Declaration
int a; // Definition
a = 10 // Initialization
int b = 10; // Definition & Initialization
Definition associates the variable with a type and allocates memory, whereas declaration just specifies the type but doesn't allocate memory. Declaration is more useful when you want to refer the variable before definition.
*Don't confuse definition with initialization. Both are different, initialization gives value to the variable. See the above example.
Following are some examples of definition.
int a;
float b;
double c;
Now function declaration :
int fun(int a,int b);
Note the semicolon at the end of function so it says it is only a declaration. Compiler knows that somewhere in the program that function will be defined with that prototype. Now if the compiler gets a function call something like this
int b=fun(x,y,z);
Compiler will throw an error saying that there is no such function. Because it doesn't has any prototype for that function.
Note the difference between two programs.
Program 1
#include <stdio.h>
void print(int a)
{
printf("%d",a);
}
main()
{
print(5);
}
In this, print function is declared and defined as well. Since function call is coming after the definition. Now see the next program.
Program 2
#include <stdio.h>
void print(int a); // In this case this is essential
main()
{
print(5);
}
void print(int a)
{
printf("%d",a);
}
It is essential because function call precedes definition so compiler must know whether there is any such function. So we declare the function which will inform the compiler.
Definition :
This part of defining a function is called Definition. It says what to do inside the function.
void print(int a)
{
printf("%d",a);
}
To understand the nouns, let's focus on the verbs first.
declare -
to announce officially; proclaim
define -
to show or describe (someone or something) clearly and completely
So, when you declare something, you just tell what it is.
// declaration
int sum(int, int);
This line declares a C function called sum that takes two arguments of type int and returns an int. However, you can't use it yet.
When you provide how it actually works, that's the definition of it.
// definition
int sum(int x, int y)
{
return x + y;
}
definition means actual function written & declaration means simple declare function
for e.g.
void myfunction(); //this is simple declaration
and
void myfunction()
{
some statement;
}
this is definition of function myfunction
Rule of thumb:
A declaration tells the compiler how to interpret the variable's data in memory. This is needed for every access.
A definition reserves the memory to make the variable existing. This has to happen exactly once before first access.
To understand the difference between declaration and definition we need to see the assembly code:
uint8_t ui8 = 5; | movb $0x5,-0x45(%rbp)
int i = 5; | movl $0x5,-0x3c(%rbp)
uint32_t ui32 = 5; | movl $0x5,-0x38(%rbp)
uint64_t ui64 = 5; | movq $0x5,-0x10(%rbp)
double doub = 5; | movsd 0x328(%rip),%xmm0 # 0x400a20
movsd %xmm0,-0x8(%rbp)
and this is only definition:
ui8 = 5; | movb $0x5,-0x45(%rbp)
i = 5; | movl $0x5,-0x3c(%rbp)
ui32 = 5; | movl $0x5,-0x38(%rbp)
ui64 = 5; | movq $0x5,-0x10(%rbp)
doub = 5; | movsd 0x328(%rip),%xmm0 # 0x400a20
movsd %xmm0,-0x8(%rbp)
As you can see nothing change.
Declaration is different from definition because it gives information used only by the compiler. For example uint8_t tell the compiler to use asm function movb.
See that:
uint def; | no instructions
printf("some stuff..."); | [...] callq 0x400450 <printf#plt>
def=5; | movb $0x5,-0x45(%rbp)
Declaration haven't an equivalent instruction because it is no something to be executed.
Furthermore declaration tells the compiler the scope of the variable.
We can say that declaration is an information used by the compiler to establish the correct use of the variable and for how long some memory belongs to certain variable.
Find similar answers here: Technical Interview Questions in C.
A declaration provides a name to the program; a definition provides a unique description of an entity (e.g. type, instance, and function) within the program. Declarations can be repeated in a given scope, it introduces a name in a given scope.
A declaration is a definition unless:
Declaration declares a function without specifying its body,
Declaration contains an extern specifier and no initializer or function body,
Declaration is the declaration of a static class data member without a class definition,
Declaration is a class name definition,
A definition is a declaration unless:
Definition defines a static class data member,
Definition defines a non-inline member function.
Declaration says "this thing exists somewhere"
int sampleFunc(); // function
extern int car; // variable
Definition says "this thing exists here; make memory for it"
int sampleFunc() {} // function
int car; // variable
Initialization is optional at the point of definition for objects, and says "here is the initial value for this thing":
int car = 0; // variable
Couldnt you state in the most general terms possible, that a declaration is an identifier in which no storage is allocated and a definition actually allocates storage from a declared identifier?
One interesting thought - a template cannot allocate storage until the class or function is linked with the type information. So is the template identifier a declaration or definition? It should be a declaration since no storage is allocated, and you are simply 'prototyping' the template class or function.
A declaration presents a symbol name to the compiler. A definition is a declaration that allocates space for the symbol.
int f(int x); // function declaration (I know f exists)
int f(int x) { return 2*x; } // declaration and definition
This is going to sound really cheesy, but it's the best way I've been able to keep the terms straight in my head:
Declaration: Picture Thomas Jefferson giving a speech... "I HEREBY DECLARE THAT THIS FOO EXISTS IN THIS SOURCE CODE!!!"
Definition: picture a dictionary, you are looking up Foo and what it actually means.
According to the GNU C library manual (http://www.gnu.org/software/libc/manual/html_node/Header-Files.html)
In C, a declaration merely provides information that a function or variable exists and gives its type. For a function declaration, information about the types of its arguments might be provided as well. The purpose of declarations is to allow the compiler to correctly process references to the declared variables and functions. A definition, on the other hand, actually allocates storage for a variable or says what a function does.
Adding definition and declaration examples from the C++ standard document(from the section 3.1 Declarations and definitions)
Definitions:
int a; // defines a
extern const int c = 1; // defines c
int f(int x) { return x+a; } // defines f and defines x
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
Declarations:
extern int a; // declares a
extern const int c; // declares c
int f(int); // declares f
struct S; // declares S
typedef int Int; // declares Int
extern X anotherX; // declares anotherX
using N::d; // declares d
The concept of Declaration and Definition will form a pitfall when you are using the extern storage class because your definition will be in some other location and you are declaring the variable in your local code file (page). One difference between C and C++ is that in C you the declarations are done normally at the beginning of a function or code page. In C++ it's not like that. You can declare at a place of your choice.
My favorite example is "int Num = 5" here your variable is 1. defined as int 2. declared as Num and 3. instantiated with a value of five. We
Define the type of an object, which may be built-in or a class or struct.
Declare the name of an object, so anything with a name has been declared which includes Variables, Funtions, etc.
A class or struct allows you to change how objects will be defined when it is later used. For example
One may declare a heterogeneous variable or array which are not specifically defined.
Using an offset in C++ you may define an object which does not have a declared name.
When we learn programming these two terms are often confused because we often do both at the same time.
Stages of an executable generation:
(1) pre-processor -> (2) translator/compiler -> (3) linker
In stage 2 (translator/compiler), declaration statements in our code tell to the compiler that these things we are going to use in future and you can find definition later, meaning is :
translator make sure that : what is what ? means declaration
and (3) stage (linker) needs definition to bind the things
Linker make sure that : where is what ? means definition
There are some very clear definitions sprinkled throughout K&R (2nd edition); it helps to put them in one place and read them as one:
"Definition" refers to the place where the variable is created or assigned storage; "declaration" refers to the places where the nature of the variable is stated but no storage is allocated. [p. 33]
...
It is important to distinguish between the declaration of an external variable and its definition. A declaration announces the properties of a variable (primarily its type); a definition also causes storage to be set aside.
If the lines
int sp;
double val[MAXVAL]
appear outside of any function, they define the external variables sp and val, cause storage to be set aside, and also serve as the declaration for the rest of that source file.
On the other hand, the lines
extern int sp;
extern double val[];
declare for the rest of the source file that sp is an int and that val is a double array (whose size is determined elsewhere), but they do not create the variables or reserve storage for them.
There must be only one definition of an external variable among all the files that make up the source program. ... Array sizes must be specified with the definition, but are optional with an extern declaration. [pp. 80-81]
...
Declarations specify the interpretation given to each identifier; they do not necessarily reserve storage associated with the identifier. Declarations that reserve storage are called definitions. [p. 210]
The declaration is when a primitive or object reference variable or method is created without assigning value or object.
int a;
final int a;
The definition means assigning the value or object respectively
int a =10;
Initialization means allocating memory for a respective variable or object.
Declaration of a variable is for informing to the compiler the following information: name of the variable, type of value it holds and the initial value if any it takes. i.e., declaration gives details about the properties of a variable. Whereas, Definition of a variable says where the variable gets stored. i.e., memory for the variable is allocated during the definition of the variable.
Declaration means give name and type to a variable (in case of variable declaration), eg:
int i;
or give name,return type and parameter(s) type to a function without body(in case of function declaration), eg:
int max(int, int);
whereas definition means assign value to a variable (in case of variable definition), eg:
i = 20;
or provide/add body(functionality) to a function is called function definition, eg:
int max(int a, int b)
{
if(a>b) return a;
return b;
}
many time declaration and definition can be done together as:
int i=20;
and:
int max(int a, int b)
{
if(a>b) return a;
return b;
}
In above cases we define and declare variable i and function max().

Nested enum and nested class have different behavior

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.

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).