I'd like to have a class that has static members to itself, but I can't figure how to do that. Is that even possible?
I get the error:
only static const integral data members can be initialized within a class
Code:
namespace misc
{
class CData
{
public:
CData( ) { };
CData( int d );
CData& operator = ( const CData& d );
static const CData FIRST = CData( 512 ); //how?
private:
int data;
};
}
As I use FIRST a lot I would like to statically access it using misc::CData::FIRST without the need to declare it somewhere in the scope. Is that by any chance possible?
... without the need to declare it somewhere in the scope. Is that by any chance possible?
No, it's not possible without declaring it (which you already tried to do in your class declaration). You probably meant, without defining it outside your class declaration. Again the answer is no.
You have to separate declaration and definition for this case (it only works with primitive integral types like int to initialize these directly in the class declaration).
First have a simple declaration in your class declaration (usually something like CData.hpp)
namespace misc {
class CData {
public:
CData( ) { };
CData( int d );
CData& operator = ( const CData& d );
static const CData& FIRST;
private:
int data;
};
}
and then define it in a separate compilation unit (usually something like CData.cpp)
namespace misc {
const CData& CData::FIRST = CData( 512 );
}
For non-integral data, something like this is preferred since it avoids the static initialization fiasco.
static const CData FIRST()
{
static CData first(512); //only initialized once, when first requested
return first;
}
... without the need to declare it somewhere in the scope. Is that by
any chance possible?
No.
C++ Standard n3337 § 9.4.2/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. The definition for a static data member shall appear in a
namespace scope enclosing the member’s class definition. (...)
You can declare a static data member in class:
namespace misc {
class CData {
public:
//...
static const CData FIRST; // declaration
//...
}
and define it in (exactly) one of the .cpp files:
namespace misc {
CData CData::FIRST = CData( 512 ); // definition
}
This is preferred solution, however you need to have this definition out of your class. You could have defined the member in class if it was of an integral type
C++ Standard n3337 § 9.4.2/3 says
If a non-volatile 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 (...)
Related
#include <iostream>
class Test {
public:
static int numelem;
Test() {}
~Test() {}
int increment();
};
int Test::numelem = 0;
int Test::increment()
{
return ++Test::numelem;
}
So I want to make a counter for my Stacks data structure.
Whenever I push, it increments and when popped it decrements.
My code works, but int Test::numelem = 0; is a global variable.
I tried using inline but unfortunately I have C++14.
I only put the static int numelem instead of the whole Stack class to focus on one feature.
Is there an alternative way I can put int Test::numelem = 0; inside the class without getting any error?
This is the typical workaround. It's particularly useful for templates.
class Test {
public:
static int& numelem() {
static int val = 0; // or your initializer here
return val;
}
int increment() {
return ++numelem();
}
};
Of course, now you're accessing it with the syntax Test::numelem() instead of just Test::numelem. But something like ++Test::numelem() still works just fine.
but int Test::numelem = 0; is a global variable.
Technically, it is not a global variable but a class static member. Functionally they behave very similarly.
Is there an alternative way I can put int Test::numelem = 0; inside the class without getting any error? unfortunately I have C++14.
With C++14 the out-of-class definition for a nonconst static data member should be in the same namespace scope where the class was defined(global namespace in your example). So there is no way of defining a nonconst static data member inside the class in c++14 as we can't use inline in c++14 and the only way of defining a nonconst static data member is to put a definition at namespace scope.
This can be seen from class.static.data:
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.
But with C++17 we can use inline to define a non-const static data member inside the class.
In MyClass below, enum MyType is defined inside the class.
In main, I create a variable of MyClass::MyType t. This compiles fine. However, when I wish to assign it a value such as OPEN, there is a compilation error "OPEN was not declared in this scope"
Firstly it probably doesn't make sense declaring an enum type inside the class and limiting its scope there and then creating a variable of that enum type elsewhere, but I'm just trying to understand what's happening.
In the first place, how am I able to create a variable of MyType in main when an object hasn't even been created? Are enums and struct types defined in a class like that implicitly static?
Also, the compiler has access to the enum code, so why doesn't it understand "OPEN"? Thanks
#include <iostream>
using namespace std;
class MyClass
{
public:
enum MyType
{
OPEN,
CLOSED
};
struct MyStruct
{
int val1;
int val2;
};
};
int main()
{
MyClass::MyType t;
t = OPEN; // compilation error
return 0;
}
Your enum MyType is inside the class, so its values are expected to be accessed through the class and the enumeration. You are already creating a MyType without instantiating the class, but an example of instantiation through the class is also provided.
class MyClass
{
public:
enum MyType
{
OPEN,
CLOSED
};
struct MyStruct
{
int val1;
int val2;
};
};
int main()
{
MyClass::MyType t; // Already a MyType value!
MyClass c; // Building your class
t = MyClass::MyType::OPEN; // No compilation error
t = c.OPEN; // Accessing enum through instantiated class
return 0;
}
Like Remy said. Value OPEN is a part of MyClass class and is only reachable in that classes scope. For your compiler to see it and use it you need to acces it through MyClass::OPEN.
(In addition to what others wrote)
If supported by the compiler (Since C++ 11),
it is a better practice to use enum class:
enum class MyType
{
OPEN,
CLOSED
};
"The enum classes (“new enums”, “strong enums”) address three problems with traditional C++ enumerations:
1) Conventional enums implicitly convert to an integer, causing errors when someone does not want an enumeration to act as an integer.
2) Conventional enums export their enumerators to the surrounding scope, causing name clashes.
3) The underlying type of an enum cannot be specified, causing confusion, compatibility problems, and makes forward declaration impossible."
ISOCPP FAQ - enum class
-
In that case, use the syntax:
int main()
{
MyClass c;
MyClass::MyType t;
t = MyClass::MyType::OPEN;
return 0;
}
See [decl.enum]/11:
Each enum-name and each unscoped enumerator is declared in the scope
that immediately contains the enum-specifier. Each scoped enumerator
is declared in the scope of the enumeration. These names obey the
scope rules defined for all names in [basic.scope] and [basic.lookup].
[ Example:
enum direction { left='l', right='r' };
void g() {
direction d; // OK
d = left; // OK
d = direction::right; // OK
}
enum class altitude { high='h', low='l' };
void h() {
altitude a; // OK
a = high; // error: high not in scope
a = altitude::low; // OK
}
— end example ] An enumerator declared in class scope can be referred
to using the class member access operators (::, . (dot) and ->
(arrow)), see [expr.ref]. [ Example:
struct X {
enum direction { left='l', right='r' };
int f(int i) { return i==left ? 0 : i==right ? 1 : 2; }
};
void g(X* p) {
direction d; // error: direction not in scope
int i;
i = p->f(left); // error: left not in scope
i = p->f(X::right); // OK
i = p->f(p->left); // OK
// ...
}
— end example ]
I create a basic IBasic interface with a static field
class IBasic
{
public:
IBasic();
virtual ~IBasic();
static std::vector< std::vector<char> > Field;
};
from which the Inherit class is inherited:
class Inherit : public IBasic
{
public:
Inherit(int);
~Inherit();
void Foo();
};
The Inherit class makes some manipulations with Field static member in constructor/or member function.
In order to create an instance of the Inherit class, we need to explicitly declare a static field in the main.cpp before the main function:
#include "Basic.h"
#include "Inherit.h"
std::vector< std::vector<char> > IBasic::Field;
int main()
{
Inherit(10);
return 0;
}
The questions are:
In what namespace does the static method actually exists (global?)? Because I know that static field/function is not a class member in fact.
Is there another way to declare this static method, for example, in a
class file, inside a main function, or through creation unnamed namespace? Is it only one right variant?
How is right? What should be considered first of all?
A static member of a class is a member of its class (that's a tautology) and its class namespace (a class is a namespace). It is not a nember of any other namespace.
A non-const static data member of a class must be defined exactly once in a program, outside of any class, in the same namespace its class is defined in (a global namespace in your case). A header file is inappropriate place for such declaration. It is normally placed in an implementation .cpp file that goes together with the header file.
Having said that, an interface should not have any static data members, much less public ones. It is most likely a grave design error.
In what namespace does the static method actually exists (global?)? Because I know that static field/function is not a class member in fact.
It is declared in scope of the class. In fact the static variable is a class member, your assumption is wrong.
Is there another way to declare this static method, for example, in a class file, inside a main function, or through creation unnamed namespace? Is it only one right variant?
The usual way is to define it in the translation unit that contains the function definitions for the class.
How is right? What should be considered first of all?
There's no right or wrong way, but as mentioned definition in the same translation unit as the class function definitions is the usual way.
Here's an example usage of a static member without any inheritance.
SomeClass.h
#ifndef SOME_CLASS_H
#define SOME_CLASS_H
class SomeClass {
private:
int x;
public:
static SomeClass* const get(); // Needed For Using class to get this pointer
SomeClass();
int getX() const { return x; }
void setX( int val ) { x = val; }
};
#endif // SOME_CLASS_H
SomeClass.cpp
#include "SomeClass.h"
static SomeClass* s_pSomeClass = nullptr;
SomeClass::SomeClass() {
s_pSomeClass = this;
}
SomeClass* const SomeClass::get() {
if ( nullptr == s_pSomeClass ) {
// throw exception
}
return s_pSomeClass;
}
Another class using above class as a static member
OtherClass.h
#ifndef OTHER_CLASS_H
#define OTHER_CLASS_H
class SomeClass; // Forward Declaration
class OtherClass {
private:
static SomeClass* pSomeClass; // The Static Member to this class
int y;
public:
OtherClass();
int getY() const { return y; }
void setY( int val ) { y = val; }
void useSomeClassToSetY();
};
#endif // OTHER_CLASS_H
OtherClass.cpp
#include "OtherClass.h"
#include "SomeClass.h"
SomeClass* OtherClass::pSomeClass = nullptr;
OtherClass::OtherClass() {
if ( nullptr == pSomeClass ) {
pSomeClass = SomeClass::get();
}
}
void OtherClass::useSomeClassToSetY() {
// First Set X To Some Value:
pSomeClass->setX( 10 ); // Use of Static Member
y = pSomeClass->getX(); // Use of Static Member
}
Static members still belong to the class, but they have static storage.
I have the following code:
class gptr_timer {
private:
static qtimer_t gptr_get_timer;
public:
static void create_gptr_get_timer(){
gptr_get_timer = qtimer_create();
}
static void destroy_gptr_get_timer(){
qtimer_destroy(gptr_get_timer);
}
static void start_gptr_get_timer(){
qtimer_start(gptr_get_timer);
}
static void stop_gptr_get_timer(){
qtimer_stop(gptr_get_timer);
}
static double get_gptr_get_time(){
return qtimer_secs(gptr_get_timer);
}
};
Which produces the following compile error:
/home/knusbau2/barnes/qppl/gptr.h:24: undefined reference to `ppl::gptr_timer::gptr_get_timer'
I'm a little confused, as I clearly have gptr_get_timer defined.
By adding a definition:
class gptr_timer { /* ... */ }; // class definition;
// *delcares* static member variables
qtimer_t gptr_timer::gptr_get_timer; // *define* static member variables
The static member definition has to go into one single translation unit, while the class definition is usually in a header. Beware.
The member definition is also the place for initialization, except in a few special cases (namely static constant expressions) which can be initialized inside the class definition.
I have the following code structure:
myClass.h
class myClass
{
public:
void DoSomething(void);
};
myClass.cpp
#include myClass.h
static const unsigned length = 5;
static myArray<float, length> arrayX;
void myClass::DoSomething(void)
{
// does something using length and array X
}
Now I want to convert the static variable defined at the file scope to be static members of the class. I do the following;
myClass.h
class myClass
{
static const unsigned length;
static myArray<float,length> arrayX;
public:
void DoSomething(void);
};
myClass.cpp
#include myClass.h
const unsigned myClass::length = 5;
myArray<float, length> myClass::arrayX;
void myClass::DoSomething(void)
{
// does something using length and array X
}
However, I get an error:
C2975: 'Length' : invalid template argument for 'myArray', expected compile-time constant expression myClass.h
I do understand I get this error because length is not initialized in the header file yet. How can I get around this?
It needs to be a constant expression, so the best you can do is move = 5 to the header.
However, I was wondering if there is a way to get around this.
Look at your code again. That myArray<float,length> is declared as a class data member in the header.
In order for the compiler to know what myClass is, it must know the full definition of that data member. But the full definition of myArray<float,length> in turn requires length to be known, because without its template arguments, myArray is not a type, but a template, and data members must be types, not class templates.
From this it's clear that, in order to have a myArray instance as a class member, the length must be known when the class is compiled, myArray is to be a member of.
Have you tried:
myArray<float, myClass::length> myClass::arrayX;
^^^^^^^^^^
You may also need to change header:
class myClass
{
static const unsigned length = 5;
and change definition of myClass::length in .cpp to not contain "= 5" (or remove it completly).