the following code in my opinion should output sum sizeof(int) + sizeof(float) + sizeof(std::string), but the storage value is always zero. Why?
struct Base {
static int IncrementID(int x) {
static int id = 0;
storage += x;
return id++;
}
static int storage;
};
int Base::storage = 0;
template<typename T>
struct Object : public Base {
static const int id;
};
template<typename T>
const int Object<T>::id(Base::IncrementID(sizeof(T)));
int main() {
Object<int> a;
Object<float> b;
Object<std::string> c;
std::cout << Base::storage;
}
You don't use those static id data members in any way that can cause their implicit instantiation. It means they don't have to be instantiated (nor their initialization has to happen). To quote the C++ standard:
[temp.inst]
3 Unless a member of a class template or a member template has
been explicitly instantiated or explicitly specialized, the
specialization of the member is implicitly instantiated when the
specialization is referenced in a context that requires the member
definition to exist; in particular, the initialization (and any
associated side effects) of a static data member does not occur unless
the static data member is itself used in a way that requires the
definition of the static data member to exist.
Doing something as simple as adding a user defined constructor like
Object() {
int i = id;
(void)i;
}
Can be enough to odr-use them, and as such force their instantiation by creating objects as you do.
Related
So inside header files I can do
namespace X {
doThis();
}
and in the implementation file I can do
namespace X {
doThis() { .... }
}
But if I have a class
class X {
public:
doThis();
};
Is it possible for me to do something like this in the implementation file
class X {
doThis() { .... }
}
instead of X::doThis() { .... }?
There's the "Java hack"¹, where you "inherit" from the class to get its members into your namespace:
class MyUserType : /*protected|private*/ X {
void foo() {
doThis(); // works
}
}
Of course this only works of
the class doesn't define additional (non-static) features that interfere when inherited
your calling code is in a class that can inherit from the type
the derived class is not a template because 2-phase lookup makes things weird again (though you can use using declarations to mitigate them, on a name-by-name basis)
Re: static data members/out of class definition
In the comments you seem to have a problem with brevity of code mostly. Look into
Disclaimer: following examples largely copied from cppreference
inline variables (c++17), which is about namespace level static variables, which also applies to class members:
constexpr/const static member initialization right in the declaration.
If a static data member of integral or enumeration type is declared const (and not volatile), it can be initialized with an initializer in which every expression is a constant expression, right inside the class definition:
struct X
{
const static int n = 1;
const static int m{2}; // since C++11
const static int k;
};
const int X::k = 3;
constexpr members are even required to have the initializer in the declaration:
struct X {
constexpr static int arr[] = { 1, 2, 3 }; // OK
constexpr static std::complex<double> n = {1,2}; // OK
constexpr static int k; // Error: constexpr static requires an initializer
};
Note that in some circumstances you may still need out-of-class definitions, but without the initializer:
If a const non-inline (since C++17) static data member or a constexpr static data member (since C++11)(until C++17²) is odr-used, a definition at namespace scope is still required, but it cannot have an initializer. A definition may be provided even though redundant (since C++17).
struct X {
static const int n = 1;
static constexpr int m = 4;
};
const int *p = &X::n, *q = &X::m; // X::n and X::m are odr-used
const int X::n; // … so a definition is necessary
constexpr int X::m; // … (except for X::m in C++17)
¹ for the longest time Java didn't have enumerations, so you'd define static constants in a base class an "inherit" from it to get the constants.
² If a static data member is declared constexpr, it is implicitly inline and does not need to be redeclared at namespace scope. This redeclaration without an initializer (formerly required as shown above) is still permitted, but is deprecated.
Minimal example and question
In this example, an inline static member variable c2 of a nontemplate class is initialized when the class member is instantiated, and member variable c1 of a template class is not. What is the difference? Why is c1 not being initialized unless I force it to be by taking its address, and c2 is initialized unconditionally?
struct C1 {
C1() { std::cerr << "C1()\n"; }
};
struct C2 {
C2() { std::cerr << "C2()\n"; }
};
template<typename T>
struct Template {
inline static C1 c1;
};
struct Nontemplate {
inline static C2 c2;
};
int main() {
Template<int> a;
Nontemplate b;
(void)a;
(void)b;
}
// Output:
C2()
Context and reasoning
Here's a bit of context to the minimal example. I have the Nontemplate class inherited from Template<something>, and the constructor of c2 depends on c1. I expect c1 to be created before c2; however, that is not the case.
template<typename T>
struct Template {
inline static C1 c1;
};
struct Nontemplate : public Template<int> {
struct C2 {
C2() {
std::cerr << "Do something with Nontemplate::C1\n";
std::cerr << "&Nontemplate::c1 = " << &Nontemplate::c1 << "\n";
}
};
inline static C2 c2;
};
int main() {
Nontemplate b;
(void)b;
}
// Output:
Do something with Nontemplate::C1
&Nontemplate::c1 = 0x600ea8
C1()
The code was compiled with g++ 7.2 with -std=c++17 flags. Both -O0 and -O2 give the same result.
Implicit instantiation of a class template only causes instantiation of the declarations it contains. Definitions are generally only instantiated when they are used in a context that requires the definition to exist.
So as long as you don't use Template<int>::c1 in a way that would require its definition to exist (i.e. by odr-using it), then it will not be defined at all.
One way of odr-using the variable is to take its address, as you mentioned.
Even if you force the instantiation of the variable, there is no guarantee when exactly it will be initialized.
C1's constructor is not constexpr and so the initialization of Nontemlate::c1 cannot be a constant expression. This means that you are going to get dynamic initialization of Template<int>::c1. Dynamic initialization of global static variables which are part of a template specialization are unordered, meaning that there is no guarantee in what order they will happen relative to any other dynamic initialization of global static variables.
Similarly Nontemlate::c2 is not initialized by a constant expression and so is also dynamically initialized. Although Nontemlate::c2 has partially ordered dynamic initialization (being an inline variable not part of a template specialization), it is still indeterminately sequenced relative to Template<int>::c1 as explained above.
It is also not strictly required that Template<int>::c1 and Nontemlate::c2 are initialized before main is entered. It is implementation-defined whether initialization is deferred until later, but before the first odr-use of the respective variable. I think this deferral is used mainly for runtime dynamic library loading, though.
A common method to avoid ordering issues of global static storage duration variables is to use a function returning a reference to a local static variable instead, i.e.:
template<typename T>
struct Template {
static auto& c1() {
static C1 instance;
return instance;
}
};
although this may have a performance impact when called often, because the construction of the local static must be checked each time.
Alternative, if the initializer can be made a constant expression, making the variable constexpr should guarantee constant initialization, meaning that no dynamic initialization will happen at all and no ordering issues will be present.
In the following program "Here" is printed:
#include <iostream>
class Base
{
static bool temp;
static bool initTemp()
{std::cout<<"Here\n";return true;}
};
bool Base::temp = Base::initTemp();
class Derived : public Base
{};
int main() {int a;std::cin>>a;}
In the following program "Here" is not printed:
#include <iostream>
template <class T>
class Base
{
static bool temp;
static bool initTemp()
{std::cout<<"Here\n";return true;}
};
template <class T>
bool Base<T>::temp = Base<T>::initTemp();
class Derived : public Base<int>
{};
int main() {int a;std::cin>>a;}
In both cases Base is never referenced. The only difference is that in the second case it is a template class. Can anyone explain to me why this behavior occurs. I am using VS 2012.
In both cases Base is never referenced.
And that is precisely the reason why you see nothing being printed to the standard output.
The definition of a class template's static data member does not get instantiated unless you use that data member; like member functions, the definitions of static data members of a class template are instantiated on demand.
This is specified in paragraph 14.7.1/1 of the C++11 Standard:
[...] The implicit instantiation of a class template specialization causes the implicit
instantiation of the declarations, but not of the definitions or default arguments, of the class member functions,
member classes, scoped member enumerations, static data members and member templates. [...]
Since your client code never refers to Base<>::temp, there is no need to construct and initialize it.
As a side note, this signature:
void main()
Is not valid (standard) C++. If you want to write portable code, the return type of main() should always be int.
In the first case, you don't instantiate Base, but you do call the static function:
bool Base::temp = Base::initTemp();
In the second case, you never instantiate the template:
template <class T>
bool Base<T>::temp = Base<T>::initTemp();
You can explicitly instantiate the Base class template, as with:
template class Base<int>;
And then you will see "Here" printed.
You cannot create variable of undefined class, which you seem to do with line:
template <class T>
bool Base<T>::temp = Base<T>::initTemp();
You cannot allocate variable of undefined type. What you need is write something like:
Base<int>::temp = value;
of cause there will be different variable allocated for each type provided, so you cannot have common static variable for a template class. You'll have separate variable for each type you instantiate your template instead.
To downvoters here is complete example:
#include <iostream>
template<class T>
class X
{
public:
static int v;
};
template<class T>
int X<T>::v = 0;
int main()
{
X<int>::v = 3;
X<char>::v = 2;
using namespace std;
cout << X<char>::v << endl << X<int>::v;
}
It prints 2 3 which means you cannot have single variable for all classes you'll instantiate your template.
how to correct it so I can display the static int by
cout<<A::a<<endl;
like in an example below?
#include <iostream>
using namespace std;
class A{
public:
static int a = 0;
};
int main()
{
cout << A::a << endl;
return 0;
}
Inside the class definition, the static members are only declared and not defined. By default, only definitions have initialization, with the exception that for static constants of integral types the declaration can have the initialization.
The problem in your program is that the static member is used (std::cout << A::a is odr-use for non-const static member attributes), but you have no definition. You need to define the variable in a single translation unit in your program by adding:
int A::a = value;
(Note that because the static member is not const, you cannot provide an initializer inside the class definition, so you need to remove the = 0 from the declaration in class definition. Also note that you can skip the = value in the initialization if value == 0, as static initialization will set A::a to 0 before any other initialization)
Either:
class A{
public:
static const int a = 0;
};
(const integral types can be initialized inside the class definition)
or
class A{
public:
static int a;
};
int A::a = 0;
Is it possible to initialize a static const value outside of the constructor? Can it be initialized at the same place where member declarations are found?
class A {
private:
static const int a = 4;
/*...*/
};
YES you can but only for int types.
If you want your static member to be any other type, you'll have to define it somewhere in a cpp file.
class A{
private:
static const int a = 4; // valid
static const std::string t ; // can't be initialized here
...
...
};
// in a cpp file where the static variable will exist
const std::string A::t = "this way it works";
Also, note that this rule have been removed in C++11, now (with a compiler providing the feature) you can initialize what you want directly in the class member declaration.
Static data members (C++ only)
The declaration of a static data member in the member list of a class is not a definition. You must define the static member outside of the class declaration, in namespace scope. For example:
class X
{
public:
static int i;
};
int X::i = 0; // definition outside class declaration
Once you define a static data member, it exists even though no objects of the static data member's class exist. In the above example, no objects of class X exist even though the static data member X::i has been defined.
Static data members of a class in namespace scope have external linkage. The initializer for a static data member is in the scope of the class declaring the member.
A static data member can be of any type except for void or void qualified with const or volatile. You cannot declare a static data member as mutable.
You can only have one definition of a static member in a program. Unnamed classes, classes contained within unnamed classes, and local classes cannot have static data members.
Static data members and their initializers can access other static private and protected members of their class. The following example shows how you can initialize static members using other static members, even though these members are private:
class C {
static int i;
static int j;
static int k;
static int l;
static int m;
static int n;
static int p;
static int q;
static int r;
static int s;
static int f() { return 0; }
int a;
public:
C() { a = 0; }
};
C c;
int C::i = C::f(); // initialize with static member function
int C::j = C::i; // initialize with another static data member
int C::k = c.f(); // initialize with member function from an object
int C::l = c.j; // initialize with data member from an object
int C::s = c.a; // initialize with nonstatic data member
int C::r = 1; // initialize with a constant value
class Y : private C {} y;
int C::m = Y::f();
int C::n = Y::r;
int C::p = y.r; // error
int C::q = y.f(); // error
The initializations of C::p and C::q cause errors because y is an object of a class that is derived privately from C, and its members are not accessible to members of C.
If a static data member is of const integral or const enumeration type, you may specify a constant initializer in the static data member's declaration. This constant initializer must be an integral constant expression. Note that the constant initializer is not a definition. You still need to define the static member in an enclosing namespace. The following example demonstrates this:
#include <iostream>
using namespace std;
struct X {
static const int a = 76;
};
const int X::a;
int main() {
cout << X::a << endl;
}
The tokens = 76 at the end of the declaration of static data member a is a constant initializer.
Just for the sake of completeness, I am adding about the static template member variables.
template<class T> struct X{
static T x;
};
template<class T> T X<T>::x = T();
int main(){
X<int> x;
}
You cannot initialize static members within constructors. Integral types you can initialize inline at their declaration. Other static members must be defined (in a .cpp) file:
// .h
class A{
private:
static const int a = 4;
static const foo bar;
...
...
};
// .cpp
const foo A::bar = ...;