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:
static
data members in class? static
arrays in class, even the const
array?static variables are specific to a class . Constructors initialize attributes ESPECIALY for an instance.
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.)
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.
Source: Stackoverflow.com