Scope of evaluation of array bound of static data member - c++

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.

Related

c++ terminology 2 part question reguarding "declaring" vs "initializing" variables [duplicate]

After reading the question, I know the differences between declaration and definition. So does it mean definition equals declaration plus initialization?
Declaration
Declaration, generally, refers to the introduction of a new name in the program. For example, you can declare a new function by describing it's "signature":
void xyz();
or declare an incomplete type:
class klass;
struct ztruct;
and last but not least, to declare an object:
int x;
It is described, in the C++ standard, at §3.1/1 as:
A declaration (Clause 7) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations.
Definition
A definition is a definition of a previously declared name (or it can be both definition and declaration). For example:
int x;
void xyz() {...}
class klass {...};
struct ztruct {...};
enum { x, y, z };
Specifically the C++ standard defines it, at §3.1/1, as:
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-specification25 (7.5) and neither an initializer nor a function- body, it declares a static data member in a class definition (9.2, 9.4), it is a class name declaration (9.1), it is an opaque-enum-declaration (7.2), it is a template-parameter (14.1), it is a parameter-declaration (8.3.5) in a function declarator that is not the declarator of a function-definition, or it is a typedef declaration (7.1.3), an alias-declaration (7.1.3), a using-declaration (7.3.3), a static_assert-declaration (Clause 7), an attribute- declaration (Clause 7), an empty-declaration (Clause 7), or a using-directive (7.3.4).
Initialization
Initialization refers to the "assignment" of a value, at construction time. For a generic object of type T, it's often in the form:
T x = i;
but in C++ it can be:
T x(i);
or even:
T x {i};
with C++11.
Conclusion
So does it mean definition equals declaration plus initialization?
It depends. On what you are talking about. If you are talking about an object, for example:
int x;
This is a definition without initialization. The following, instead, is a definition with initialization:
int x = 0;
In certain context, it doesn't make sense to talk about "initialization", "definition" and "declaration". If you are talking about a function, for example, initialization does not mean much.
So, the answer is no: definition does not automatically mean declaration plus initialization.
Declaration says "this thing exists somewhere":
int foo(); // function
extern int bar; // variable
struct T
{
static int baz; // static member variable
};
Definition says "this thing exists here; make memory for it":
int foo() {} // function
int bar; // variable
int T::baz; // static member variable
Initialisation is optional at the point of definition for objects, and says "here is the initial value for this thing":
int bar = 0; // variable
int T::baz = 42; // static member variable
Sometimes it's possible at the point of declaration instead:
struct T
{
static int baz = 42;
};
…but that's getting into more complex features.
For C, at least, per C11 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, is the (only) declaration of the identifier;
for a typedef name, is the first (or only) declaration of the identifier.
Per C11 6.7.9.8-10:
An initializer specifies the initial value stored in an object ... if
an object that has automatic storage is not initialized explicitly,
its value is indeterminate.
So, broadly speaking, a declaration introduces an identifier and provides information about it. For a variable, a definition is a declaration which allocates storage for that variable.
Initialization is the specification of the initial value to be stored in an object, which is not necessarily the same as the first time you explicitly assign a value to it. A variable has a value when you define it, whether or not you explicitly give it a value. If you don't explicitly give it a value, and the variable has automatic storage, it will have an initial value, but that value will be indeterminate. If it has static storage, it will be initialized implicitly depending on the type (e.g. pointer types get initialized to null pointers, arithmetic types get initialized to zero, and so on).
So, if you define an automatic variable without specifying an initial value for it, such as:
int myfunc(void) {
int myvar;
...
You are defining it (and therefore also declaring it, since definitions are declarations), but not initializing it. Therefore, definition does not equal declaration plus initialization.
"So does it mean definition equals declaration plus initialization."
Not necessarily, your declaration might be without any variable being initialized like:
void helloWorld(); //declaration or Prototype.
void helloWorld()
{
std::cout << "Hello World\n";
}

why initialization of static member variable is not allowed inside class but initialization of const static members is allowed?

When I tried to initialize the static member variables at the time of declaration inside the class, compiler is throwing error as expected because we need to explicitly allocate the space for static member variables outside the class. I thought this should be same for static const variables. But to my surprise, initialization of static const member variables inside the class is working fine. Can any one please let me know why normal static member variable initialization is not allowed in the same way?
I assume that you meant
// inside class definition block
static const int a = 0;
static int b = 0; // error
C++ Standard 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 name- space scope if it is used in the program and the namespace scope definition shall not contain an initializer.
It is specified in standard.
Edit:
as M.M pointed out, above quotation is actually not what the Standard says, and the correct one is C++ Standard 12.2.3.2/3
If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration
in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an
assignment-expression is a constant expression (8.20). The member shall still be defined in a namespace scope
if it is odr-used (6.2) in the program and the namespace scope definition shall not contain an initializer. An
inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer .
If the member is declared with the constexpr specifier, it may be redeclared in namespace scope with no
initializer (this usage is deprecated; see D.1). Declarations of other static data members shall not specify a
brace-or-equal-initializer.
One needs a bit of space in memory. Const do not - they can be hard coded.
classes are usually declared in header files. Header files can be included multiple times in body files. If a static member, which needs memory, is defined within a class, than there will be a different copy of such a member in different body files. This will kill the idea of static members.
Constants on the other hand do not use memory and are compile-only constructs. therefore declaring them in the classes does not do any harm.

Why are classes not considered completely defined within static data member initializer?

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];
};

Inner member visibility in class

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.

Are static class members guaranteed to be initialized before `main` is called?

Is there any guarantee that static class members are initialized before main is called?
I think no:
[C++03: 3.6.2/3]: It is implementation-defined whether or not the dynamic initialization (8.5, 9.4, 12.1, 12.6.1) of an object of namespace scope is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first use of any function or object defined in the same translation unit as the object to be initialized.
Hmm, really?
Well, arguably, "defined in namespace scope" is not quite the same thing as "an object of namespace scope":
[C++03: 9.4.2/2]: The declaration of a static data member in its class definition is not a definition and may be of an incomplete type other than cv-qualified void. The definition for a static data member shall appear in a namespace scope enclosing the member's class definition. In the definition at namespace scope, the name of the static data member shall be qualified by its class name using the :: operator. The initializer
expression in the definition of a static data member is in the scope of its class (3.3.6).
However, it's the initializer that's in the class's scope; there's no mention of the static member itself having anything other than namespace scope (unless we mentally inject the word "lexically" everywhere).
There is this pleasing paragraph:
[C++03: 9.4.2/7]: Static data members are initialized and destroyed exactly like non-local objects (3.6.2, 3.6.3).
However, unfortunately, the only further definition of the sequencing of main and static initialisation, with respect to "non-local objects", is the aforementioned [C++03: 3.6.2/3].
So what then?
I believe that the intent of this otherwise potentially ambiguous rule is clearly shown by the new wording in C++11, which resolves everything:
[C++11: 9.4.2/6]: Static data members are initialized and destroyed exactly like non-local variables (3.6.2, 3.6.3).
[C++11: 3.6.2/4]: It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main. [..]
C++03: In short, no guarantee
C++11: No guarantee, see Lightness' answer.
My interpretation/analysis of the C++03 statements:
Terminology: [basic.start.init]/1
Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization.
Order of initialization on non-local objects:
Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place.
But it doesn't mention when "any other initialization" takes place, i.e. there's no guarantee it'll be before the first statement of main, even for zero-initialization.
Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.
But again, no guarantee.
Dynamic initialization
[basic.start.init]/3
It is implementation-defined whether or not the dynamic initialization (8.5, 9.4, 12.1, 12.6.1) of an object of namespace scope is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first use of any function or object defined in the same translation unit as the object to be initialized.
But what is an "object of namespace scope"? I have not found any clear definition in the Standard. scope is actually a property of a name, not of an object. Therefore we could read this as "object defined in namespace scope" or "object introduced by a name of namespace scope". Note the reference "9.4" after dynamic initialization. It refers to "Static members", which can only mean static data members. So I'd say it means "object defined at namespace scope", as static data members are defined at namespace scope:
[class.static.data]/2
The definition for a static data member shall appear in a namespace scope enclosing the member’s class definition.
Even if you don't agree on this interpretation, there's still
[basic.start.init]/1
Objects with static storage duration defined in namespace scope in the same translation
unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.
This clearly applies to static data members, which means that they cannot be initialized differently than objects introduced by names of namespace scope if there's such an object before the definition of the static data member. That is, if there was no guarantee at all on the dynamic initialization of static data members, the guarantees of any preceding object introduced by a name of namespace scope would apply - which are: none (it does not have to be initialized before the first statement of main).
If there's no such object preceding the definition of the static data member and you disagree on the interpretation - there would be no guarantee on the dynamic initialization of static data members at all.
Conclusion
So we only have a guarantee that dynamic initialization happens sometime (before any usage) plus an exception that initialization with side-effects must not be eliminated. Still, we have no guarantee that any kind of initialization of non-local objects is performed before the first statement of main.
Note: There are workarounds, like:
#include <iostream>
struct my_class
{
static int& my_var()
{
static int i = 42;
return i;
}
};
int j = ++my_class::my_var();
int k = ++my_class::my_var();
int main()
{
std::cout << j << " : " << k << std::endl;
}