Where in a declaration may a storage class specifier be placed? - c++

For example, let's consider the static storage class specifier. Here are a few examples of both valid and ill-formed uses of this storage class specifier:
static int a; // valid
int static b; // valid
static int* c; // valid
int static* d; // valid
int* static e; // ill-formed
static int const* f; // valid
int static const* g; // valid
int const static* h; // valid
int const* static i; // ill-formed
typedef int* pointer;
static pointer j; // valid
pointer static k; // valid
(The declarations marked "valid" were accepted by Visual C++ 2012, g++ 4.7.2, and Clang++ 3.1. The declarations marked "ill-formed" were rejected by all of those compilers.)
This seems odd because the storage class specifier applies to the declared variable. It is the declared variable that is static, not the type of the declared variable. Why are e and i ill-formed, but k is well-formed?
What are the rules that govern valid placement of storage class specifiers? While I've used static in this example, the question applies to all storage class specifiers. Preferably, a complete answer should cite relevant sections of the C++11 language standard and explain them.

In summary, anywhere in the declaration specifier (See section 7.1 in the ISO/IEC 14882-2012), ie before the *. Qualifiers after the * are associated with the pointer declarator, not the type specifier, and static doesn't make sense within the context of a pointer declarator.
Consider the following cases:
You can declare a normal int and a pointer to an int in the same declaration list, like this:
int a, *b;
this is because the type specifier is int, then you have two declarations using that type specifier int, a, and a pointer declarator *a which declares a pointer to int. Now consider:
int a, static b; // error
int a, *static b; // error
int a, static *b; // error
which should look wrong (as they are), and the reason (as defined in sections 7.1 and 8.1) is because C and C++ require that your storage specifiers go with your type specifier, not in your declarator.
So now it should be clear that that the following is also wrong, since the above three are also wrong:
int *static a; // error
Your last example,
typedef int* pointer;
static pointer j; // valid
pointer static k; // valid
are both valid and both equivalent because the pointer type is defined as a type specifier and you can put your type specifier and storage specifeir in any order. Note that they are both equivalent and would be equivalent to saying
static int *j;
static int *k;
or
int static *j;
int static *k;

Per 7.1, the [simplified] structure of C++ declaration is
decl-specifier-seq init-declarator-list;
Per 7.1/1, storage class specifiers belong in the initial "common" part decl-specifier-seq.
Per 8/1, init-declarator-list is a sequence of declarators.
Per 8/4, the * part of pointer declaration is a part of an individual declarator in that sequence. This immediately means that everything that follows a * is a part of that individual declarator. This is why some of your storage class specifier placements are invalid. Declarator syntax does not allow inclusion of storage class specifiers.
The rationale is rather obvious: since storage class specifiers are supposed to apply to all declarators in the whole declaration, they are placed into the "common" part of the declaration.
I'd say that a more interesting (and somewhat related) situation takes place with specifiers that can be present in both decl-specifier-seq and individual declarators, like const specifier. For example, in the following declaration
int const *a, *b;
does const apply to all declarators or only to the first one? The grammar dictates the former interpretation: that const applies to all declarators, i.e. it is a part of the decl-specifier-seq.

If you employ the "Golden Rule" (which also doesn't apply only to pointers) it follows naturally, intuitively, and it avoids a lot of mistakes and pitfalls when declaring variables in C/C++. The "Golden Rule" should not be violated (there are rare exceptions, like const applied to array typedefs, which propagates const to the base type, and references, that came with C++).
K&R, Appendix A, Section 8.4, Meaning of Declarators states:
Each declarator is taken to be an assertion that when a construction of the same form as the declarator appears in an expression, it yields an object of the indicated type and storage class.
To declare a variable in C/C++ you should really think of the expression you should apply to it to get the base type.
1) There should be a variable name
2) Then comes the expression as valid* out of the declaration statement, applied to the variable name
3) Then comes the remaining information and properties of declaration like base type and storage
Storage is not a characteristic you can always confer to the outcome of expressions, contrary to constness for example. It makes sense only at declaration. So storage must come somewhere else that's not in 2.
int * const *pp;
/*valid*/
int * static *pp;
/*invalid, this clearly shows how storage makes no sense for 2 and so breaks */
/*the golden rule. */
/*It's not a piece of information that goes well in the middle of a expression.*/
/*Neither it's a constraint the way const is, it just tells the storage of */
/*what's being declared. */
I think K&R wanted us to use inverted reasoning when declaring variables, it's frequently not the common habit. When used, it avoids most of complex declaration mistakes and difficulties.
*valid is not in a strict sense, as some variations occur, like x[], x[size, not indexing], constness, etc... So 2 is a expression that maps well (for the declaration usage), "same form", one that reflects variable's use, but not strictly.
Golden Rule Bonus for the Uninitiated
#include <iostream>
int (&f())[3] {
static int m[3] = {1, 2, 3};
return m;
}
int main() {
for(int i = 0; i < sizeof(f()) / sizeof(f()[0]); ++i)
std::cout << f()[i] << std::endl;
return 0;
}
In the context of declarations, & is not an operation to get an address, it just tells what's a reference.
f(): f is a function
&return: its return is a reference
reference[3]: the reference is to an array of 3 elements
int array[i]: an element is an int
So you have a function that returns a reference to an array of 3 integers, and as we have the proper compile time information of the array size, we can check it with sizeof anytime =)
Final golden tip, for anything that can be placed before the type, when in multiple declarations, it's to be applied to all the variables at once, and so can't be applied individually.
This const can't be put before int:
int * const p;
So the following is valid:
int * const p1, * const p2;
This one can:
int const *p; // or const int *p;
So the following is invalid:
int const *p1, const *p2;
The exchangeable const is to be applied for all:
int const *p1, *p2; // or const int *p1, *p2;
Declaration Conventions
Because of that, I always put everything that can't be put before the type, closer to the variable (int *a, int &b), and anything that can be put before, I put before (volatile int c).
There's much more on this topic at http://nosubstance.me/post/constant-bikeshedding/.

Related

Is the const return type required in both function declaration and definition?

const int test();
int test(){
return 5;
}
int main(){
return 0;
}
Above does not compile in C++ with this error message:
error: ambiguating new declaration of 'int test()'
However it does compile fine in C. Knowing these are 2 very different languages, I was wondering if there's a specific feature in C++ which requires it
to have const return types in both the definition and declaration?
I was wondering if there's a specific feature in C++ which requires it
to have const return types in both the definition and declaration?
No. Actually, it's the other way around. There is a specific feature in C that drops that requirement.
From C documentation of function declarations # cppreference.com:
The return type cannot be cvr-qualified: any qualified return type is adjusted to its unqualified version for the purpose of constructing the function type.
In your C example, even though you think you declared const int test();, the const qualification is dropped and you actually declared int test();. Hence declaration and definition match.
C++ does not drop cv-qualification from return types.
Yes, cv-qualified return type is different from a non-qualified return type, so those declarations conflict.
Note that it isn't ever useful to return a cv-qualified non-class object because the qualifiers will have no effect on the returned value. For classes, it does affect the type of the value and thus affects which member functions can be called (but cv qualified class return types are still rarely useful, if ever).
C++ has references.
In C you're always going to get a copy of the return value, so it doesn't matter so much if it's const or not.
int x;
x = test();
In C++ you can capture the return value with a reference, and it makes a big difference if the reference is const or not.
int &x = test();
const int &y = test();
So the compiler needs to know exactly what you're returning.

Is, or is not, in-class initialization of static variables C++ 11 compliant? [duplicate]

Why can't I initialize non-const static member or static array in a class?
class A
{
static const int a = 3;
static int b = 3;
static const int c[2] = { 1, 2 };
static int d[2] = { 1, 2 };
};
int main()
{
A a;
return 0;
}
the compiler issues following errors:
g++ main.cpp
main.cpp:4:17: error: ISO C++ forbids in-class initialization of non-const static member ‘b’
main.cpp:5:26: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:5:33: error: invalid in-class initialization of static data member of non-integral type ‘const int [2]’
main.cpp:6:20: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:6:27: error: invalid in-class initialization of static data member of non-integral type ‘int [2]’
I have two questions:
Why can't I initialize static data members in class?
Why can't I initialize static arrays in class, even the const array?
Why I can't initialize static data members in class?
The C++ standard allows only static constant integral or enumeration types to be initialized inside the class. This is the reason a is allowed to be initialized while others are not.
Reference:
C++03 9.4.2 Static data members
§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.
What are integral types?
C++03 3.9.1 Fundamental types
§7
Types bool, char, wchar_t, and the signed and unsigned integer types are collectively called integral types.43) A synonym for integral type is integer type.
Footnote:
43) Therefore, enumerations (7.2) are not integral; however, enumerations can be promoted to int, unsigned int, long, or unsigned long, as specified in 4.5.
Workaround:
You could use the enum trick to initialize an array inside your class definition.
class A
{
static const int a = 3;
enum { arrsize = 2 };
static const int c[arrsize] = { 1, 2 };
};
Why does the Standard does not allow this?
Bjarne explains this aptly here:
A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.
Why are only static const integral types & enums allowed In-class Initialization?
The answer is hidden in Bjarne's quote read it closely,
"C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects."
Note that only static const integers can be treated as compile time constants. The compiler knows that the integer value will not change anytime and hence it can apply its own magic and apply optimizations, the compiler simply inlines such class members i.e, they are not stored in memory anymore, As the need of being stored in memory is removed, it gives such variables the exception to rule mentioned by Bjarne.
It is noteworthy to note here that even if static const integral values can have In-Class Initialization, taking address of such variables is not allowed. One can take the address of a static member if (and only if) it has an out-of-class definition.This further validates the reasoning above.
enums are allowed this because values of an enumerated type can be used where ints are expected.see citation above
How does this change in C++11?
C++11 relaxes the restriction to certain extent.
C++11 9.4.2 Static data members
§3
If a static data member is of const literal 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. A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. —end note ] 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.
Also, C++11 will allow(§12.6.2.8) a non-static data member to be initialized where it is declared(in its class). This will mean much easy user semantics.
Note that these features have not yet been implemented in latest gcc 4.7, So you might still get compilation errors.
This seems a relict from the old days of simple linkers. You can use static variables in static methods as workaround:
// header.hxx
#include <vector>
class Class {
public:
static std::vector<int> & replacement_for_initialized_static_non_const_variable() {
static std::vector<int> Static {42, 0, 1900, 1998};
return Static;
}
};
int compilation_unit_a();
and
// compilation_unit_a.cxx
#include "header.hxx"
int compilation_unit_a() {
return Class::replacement_for_initialized_static_non_const_variable()[1]++;
}
and
// main.cxx
#include "header.hxx"
#include <iostream>
int main() {
std::cout
<< compilation_unit_a()
<< Class::replacement_for_initialized_static_non_const_variable()[1]++
<< compilation_unit_a()
<< Class::replacement_for_initialized_static_non_const_variable()[1]++
<< std::endl;
}
build:
g++ -std=gnu++0x -save-temps=obj -c compilation_unit_a.cxx
g++ -std=gnu++0x -o main main.cxx compilation_unit_a.o
run:
./main
The fact that this works (consistently, even if the class definition is included in different compilation units), shows that the linker today (gcc 4.9.2) is actually smart enough.
Funny: Prints 0123 on arm and 3210 on x86.
It's because there can only be one definition of A::a that all the translation units use.
If you performed static int a = 3; in a class in a header included in all a translation units then you'd get multiple definitions. Therefore, non out-of-line definition of a static is forcibly made a compiler error.
Using static inline or static const remedies this. static inline only concretises the symbol if it is used in the translation unit and ensures the linker only selects and leaves one copy if it's defined in multiple translation units due to it being in a comdat group. const at file scope makes the compiler never emit a symbol because it's always substituted immediately in the code unless extern is used, which is not permitted in a class.
One thing to note is static inline int b; is treated as a definition whereas static const int b or static const A b; are still treated as a declaration and must be defined out-of-line if you don't define it inside the class. Interestingly static constexpr A b; is treated as a definition, whereas static constexpr int b; is an error and must have an initialiser (this is because they now become definitions and like any const/constexpr definition at file scope, they require an initialiser which an int doesn't have but a class type does because it has an implicit = A() when it is a definition -- clang allows this but gcc requires you to explicitly initialise or it is an error. This is not a problem with inline instead). static const A b = A(); is not allowed and must be constexpr or inline in order to permit an initialiser for a static object with class type i.e to make a static member of class type more than a declaration. So yes in certain situations A a; is not the same as explicitly initialising A a = A(); (the former can be a declaration but if only a declaration is allowed for that type then the latter is an error. The latter can only be used on a definition. constexpr makes it a definition). If you use constexpr and specify a default constructor then the constructor will need to be constexpr
#include<iostream>
struct A
{
int b =2;
mutable int c = 3; //if this member is included in the class then const A will have a full .data symbol emitted for it on -O0 and so will B because it contains A.
static const int a = 3;
};
struct B {
A b;
static constexpr A c; //needs to be constexpr or inline and doesn't emit a symbol for A a mutable member on any optimisation level
};
const A a;
const B b;
int main()
{
std::cout << a.b << b.b.b;
return 0;
}
A static member is an outright file scope declaration extern int A::a; (which can only be made in the class and out of line definitions must refer to a static member in a class and must be definitions and cannot contain extern) whereas a non-static member is part of the complete type definition of a class and have the same rules as file scope declarations without extern. They are implicitly definitions. So int i[]; int i[5]; is a redefinition whereas static int i[]; int A::i[5]; isn't but unlike 2 externs, the compiler will still detect a duplicate member if you do static int i[]; static int i[5]; in the class.
I think it's to prevent you from mixing declarations and definitions. (Think about the problems that could occur if you include the file in multiple places.)
static variables are specific to a class . Constructors initialize attributes ESPECIALY for an instance.

Getting sizeof(MyClass) in class member declaration

I am working in embedded C++ project, where I plan to make memory statically allocated as much as I can. So, I am writing a set of functions to override new/delete for all classes and for the global new/delete.
Here is a naive implementation:
class MyClass
{
int x;
float y;
double z;
static MyClass m_preAllocatedObjects[100]; //Solution 1
static char m_preAllocatedMemory[100 * sizeof(MyClass)]; //Solution 2
static char* getPreAllocatedMemory() // Solution 3
{
static char localStaticMemory[100 * sizeof(MyClass)];
return localStaticMemory;
}
static void* operator new(size_t s){
void* p; /*fill p from the pre-allocated memory or object*/;
return p;
}
};
Solution 1: It works for object with default constructor only.
Solution 2: It gives compilation error use of undefined type 'MyClass'; and this is what I am asking about.
Solution 3: This solution works fine.
The question is:
Why can I create static members of MyClass, while I can't get sizeof(MyClass)?
while I can't get sizeof(MyClass)?
The reason is that MyClass is not fully defined until the closing } of the class definition, from section 9 Classes of the c++11 standard (draft n3337):
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.
For purposes of access checking, the injected-class-name is treated as if it were a public member name. A
class-specifier is commonly referred to as a class definition. A class is considered defined after the closing
brace of its class-specifier has been seen even though its member functions are in general not yet defined.
The optional attribute-specifier-seq appertains to the class; the attributes in the attribute-specifier-seq are
thereafter considered attributes of the class whenever it is named.
and sizeof can only be applied to a complete type, from section 5.3.3 Sizeof:
The sizeof operator yields the number of bytes in the object representation of its operand. The operand is
either an expression, which is an unevaluated operand (Clause 5), or a parenthesized type-id. The sizeof
operator shall not be applied to an expression that has function or incomplete type, to an enumeration
type whose underlying type is not fixed before all its enumerators have been declared, to the parenthesized
name of such types, or to an lvalue that designates a bit-field. ...
To correct, define the size of the array outside of the class definition:
class MyClass
{
static char m_preAllocatedMemory[];
};
char MyClass::m_preAllocatedMemory[100 * sizeof(MyClass)];
To "know" the size of MyClass the compiler will need to know the whole class. When you are forward declaring a static MyClass m_preAllocatedObjects[100]; you are actually not defining the variable - to actually get the variable, you will have to do :
MyClass MyClasss::m_preAllocatedObjects[100];
somewhere in a .cpp file. This is where the memory for the objects are being "placed" as a lump in the data-segment. So the compiler doesn't have to know the actual size of MyClass at the point where you declare the variable. However, it does need to know the size to evaluate 100 * sizeof(MyClass).
You have to do that in two step:
declaring an array (without knowing its size) inside the class.
defining it, after you finish declaring the class, so that you know the size.
Here is the code:
class MyClass
{
int x;
...
static char m_preAllocatedMemory[];
};
char MyClass::m_preAllocatedMemory[100 * sizeof(MyClass)];

const in C vs const in C++

The given code compiles in C but fails in C++.
int main()
{
const int x; /* uninitialized const compiles in C but fails in C++*/
}
What is the rationale and the reason behind the change from C to C++?
See the spec, in the compatibility appendix C.1.6:
7.1.6 [see also 3.5]
Change: const objects must be initialized in C++ but can be left uninitialized in C
Rationale: A const object cannot be assigned to so it must be initialized to hold a useful value.
Effect on original feature: Deletion of semantically well-defined feature.
Difficulty of converting: Semantic transformation.
How widely used: Seldom.
Note that there is a legitimate use of an uninitialized, const-qualified object of automatic storage duration: its address can be taken and used as a unique key for labeling recursion levels in a recursive function. This is somewhat obscure, but worth noting. C makes this use efficient, while C++ requires you waste time and code size on initializing it. (In theory the compiler could perhaps determine that the value is never used and optimize out the initialization, but since you're passing around a pointer, that would be rather difficult to prove.)
The const keyword was introduced to C in C89 in 1989, but had been with C++ since its creation in 1983. So it was "backported" from C++ to C.
Initialization semantics are generally different in C and C++. Although most of the time they "just do the thing you expect", there are cases where the differences become quite important. C++ really isn't a superset of C after all.
For example, in C++ you can't:
goto x;
int i = 3;
x:
puts("Hello, world");
But that is perfectly legal in C.
The ISO standard says (in 8.5 [dcl.init] paragraph 9):
If no initializer is specified for an object, and the object is of
(possibly cv-qualified) non-POD class type (or array thereof), the
object shall be default-initialized; if the object is of
const-qualified type, the underlying class type shall have a
user-declared default constructor.
if you try the same example after modifying to this:
int main()
{
/*Unless explicitly declared extern, a const object does not have
external linkage and must be initialized*/
extern const int x;
return 0;
}
it will get compiled. So this self explains the need of enforcing this error to c++, declaring const vars without initializing and extern linkage is of no use, so coder must have added it by mistake.
#include<iostream>
using namespace std;
class Test
{
int value;
public:
Test(int v = 0) {value = v;}
int getvalue() const {return value;}
};
int main(){
Test t(20);
cout << t.getvalue();
return 0;
const double P1 = 68.68;
cout<<P1<<endl;
int star = 57;
int const *pstar = &star;
cout<<*pstar<<endl;
return 0;
}

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