Should we define a static const member outside of the class definition even if it is initialised inside the class?
#include<iostream>
using namespace std;
class abc
{
static const int period=5;
int arr[period];
public:
void display()
{
cout<<period<<endl;
}
};
const int abc::period;
int main()
{
abc a;
a.display();
return 0;
}
After commenting // const int abc::period;, both versions of the code run fine on gcc 4.3.4. So I want to ask why do both versions work and which one is standard compliant?
You are defining the static memberperiod by writing const int abc::period;. You are allowed to provide an in class initializer for static const member of a class but that's not definition, but that's merely a declaration.
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.
Your code compiles even without the definition because you are not taking the address of the static member. Bjarne Stroustrup mentions in the C++-FAQ here that You can take the address of a static member if (and only if) it has an out-of-class definition
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.
I'm reading Scott Meyers' C++ and come across this example:
class GamePlayer{
private:
static const int NumTurns = 5;
int scores[NumTurns];
// ...
};
What you see above is a declaration for NumTurns, not a definition.
Why not a definition? It looks like we initialize the static data member with 5.
I just don't understand what it means to declare but not define a variable with the value 5. We can take the address of the variable fine.
class A
{
public:
void foo(){ const int * p = &a; }
private:
static const int a = 1;
};
int main ()
{
A a;
a.foo();
}
DEMO
Because it isn't a definition. Static data members must be defined outside the class definition.
[class.static.data] / 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.
As for taking the address of your static member without actually defining it, it will compile, but it shouldn't link.
you need to put a definition of NumTurns in source file, like
const int GamePlayer::NumTurns;
Please consider the following mini example
// CFoo.hpp
class CFoo{
private:
static const double VPI = 0.5;
public:
double getVpi();
};
// CFoo.cpp
#include "CFoo.hpp"
double CFoo::getVpi(){
double x = -VPI;
return x;
}
// main.cpp
#include "CFoo.hpp"
int main(){
CFoo aFoo();
return 0;
}
Lining the program with gcc version 4.5.1 produces the error CFoo.cpp: undefined reference to CFoo::VPI. The error dose not occur if
VPI is not negated
the negation is written as double x = -1 * VPI;
Declaration and definition of class CFoo happen in the same file
Do you know the reason for this error?
There are multiple problems with your code. Primarily, this is not valid C++03:
class CFoo{
private:
static const double VPI = 0.5;
// ...
};
The declaration of a static data member can specify a constant initializer if and only if that initializer is const integral or const enumeration type. 0.5 is neither of these, and hence your code is not valid C++. 9.4.2 Static data members covers this:
2/ The declaration of a static data member in its class definition is
not a definition [...]The definition for a static data member shall
appear in a namespace scope enclosing the member’s class definition.
[...]
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 order to initialize VPI, you must do so in the CPP file:
header:
class CFoo{
private:
static const double VPI;
};
cpp :
const double CFoo::VPI = 0.5;
Another problem, unrelated, is here:
int main(){
CFoo aFoo(); // NOT OK
return 0;
The expression CFoo aFoo(); doesn't do what you think it does. You think it declares an object aFoo of type CFoo and initializes it using CFoo's default constructor. But what it actually does is declare a function named aFoo taking no parameters, returning a CFoo by value. This is known as the most vexing parse. In order to do what you want, simple omit the parenthesis:
CFoo aFoo;
Is it possible to initialize a static constant member in a class definition? Please see below for the code,
class foo
{
public:
foo(int p) : m_p(p){}
~foo(){}
private:
int m_p;
};
class bar
{
public:
bar(){}
~bar(){}
public:
static const foo m_foo = foo( 2 ); //is this possible?
};
Many thanks.
Short answer:
No, until the static member is const and is of integral or enumeration type.
Long answer:
$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."
Not for a static data member of class type, as in your example.
9.4.2/2:
The declaration of a static data
member in its class definition is not
a definition ... The definition for
a static data member shall appear in a
namespace scope enclosing the member’s
class definition.
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.
I have this code (question from exam) and there are many errors in the code, here is the code:
class A
{
int z;
static int y;
public:
A() {(*this).incrementY();}
int x () { y=y+2; return z;}
static int getY(){ return z;}
void incrementY() {y++}
};
class B: A
{
public:
B (): A() {(*this).incrementY();};
};
class C : public B
{
public:
C(): B() {(*this).incrementY;() }
};
int main()
{
A::x();
A a;
a.getY();
A::getY();
B b;
C c;
C::getY();
b.x();
}
There is a private inheritance. This means that all the methods and variables in B become private to any class that inherits from B?
There are so many that you cannot but get tired. Here are some:
void incrementY() {y++}
No ; after y++.
24. A::x();
Non static member cannot be invoked via a class name.
25. A a;
No definition of static member y. If y was a const this would've been okay. This is a bit tricky so I'll quote.
9.4.2 Static data members
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 [...]
If a static data member is of const effective literal type, its declaration in the class definition can specify
a constant-initializer brace-or-equal-initializer with an initializer-clause that is an integral constant expression.
A static data member of effective literal type can be declared in the class definition with the
constexpr specifier; if so, its declaration shall specify a constant-initializer brace-or-equal-initializer with an initializer-clause that is an integral constant expression. In both these cases, the member may 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.
26. a.getY();
27. A::getY();
Illegal reference to non-static member A::z.
Taken care of by first observation.
28. B b;
29. C c;
30. C::getY();
getY() is a private member of B, not visible to C, let alone be public.
31. b.x();
The member function x() inherited from A is private.
You are correct there (although it actually means that all A's methods become inaccessible).
However, there are a few other problems:
A::x() // will not work as x is not static.
a.getY() // will not work as getY() is static.
C::getY() // Cannot access getY()
Yes, that is correct, although you could just compile it with any number of online C++ compilers to verify.