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).
Related
I have a hyerarchy of classes. Methods of these classes may create temporary static arrays of the same size. I want to set size as static const field of the base class.
I put declaration of the field in a heared file and initialized it in a source file. This works without issues when compiled using GCC 4.3 but fails with VS compiler.
Base.h
class Base
{
public:
virtual void function();
protected:
static const int size;
};
Base.cpp
#include "Base.h"
const int Base::size = 128;
void Base::function()
{
int array[size];
}
Derived.h
#include "Base.h"
class Derived : public Base
{
void function();
};
Derived.cpp
#include "Derived.h"
void Derived::function()
{
int array[size];
}
Main.cpp
#include "Derived.h"
int main()
{
Base* object = new Derived();
object->function();
return 0;
}
I expected that size would be initialized in Base.cpp and considered as const in Derived.cpp. But it works only with GCC compiler.
Visual Studio shows the following error messages:
error C2131: expression did not evaluate to a constant
note: failure was caused by non-constant arguments or reference to a non-constant symbol
note: see usage of 'size'
A constant variable can only be used in a constant expression after its initialisation has been encountered (and is a constant initialiser). Base::size hasn't been initialised in Derived.cpp, so it cannot be used in a constant expression (such as length of the array): The program is ill-formed.
How to use static const field of a base class as size of an array in functions of a derived class?
Initialise the size within the class declaration, and declare it constexpr.
As pointed out in the comments, a GCC extension makes this compile successfully. The problem is easily fixable by removing
const int Base::size = 128;
And change the Base::size (in .h) to
static constexpr int Base::size = 128;
This makes sure Base::size can be evaluated at compile-time. If you want to be have more influence on the value of size, templates can be used:
template <int N>
class Base {
protected: static constexpr int size = N;
};
called through
Base<10>::size; // returns 10
Base<128>::size; // returns 128
using my_base = Base<128>;
my_base::size; // returns 128
I want to declare the length of an array member variable using a constant static variable of the class. If I do:
// A.h
#include <array>
using namespace std;
class A {
array<int,LENGTH> internalArray;
public:
const static int LENGTH;
};
// A.cpp
#include "A.h"
constexpr int A::LENGTH{10};
There is the error in A.h: "'LENGTH' was not declared in this scope", when declaring internalArray.
I find it weird because how come a class member variable, i.e. LENGTH, is out of scope inside the class? The only workaround I found was to move the initialization from A.cpp to A.h:
// A.h
#include <array>
using namespace std;
constexpr int LENGTH{10};
class A {
array<int,LENGTH> internalArray;
public:
const static int LENGTH;
};
But as I understand, first these are two different variables: the global namespace scope LENGTH and the class scope LENGTH. Also, declaring a variable in .h (outside class A) will create an independent LENGTH object in every translation unit where the header is included.
Is there a way to specify the length of the array with a static class-scoped variable?
Try this:
#include <array>
class A {
public:
static const size_t LENGTH = 12;
private:
std::array<int,LENGTH> internalArray;
};
int main(){
A a;
}
You can declare the value of LENGTH right in your class header, no need to have it be a separate global variable or for it to live in the cpp file.
Use the size_t type, since that is what the std::array template expects.
If that public/private arrangement is bad for you, know that you can include multiple public/private indicators in the class header.
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 (...)
I have the following code (it's on ideone.com):
template<class T>
class CMemoryPool
{
public:
CMemoryPool(int param1)
: stuff(param1)
{}
private:
T stuff;
};
template<class T>
class CList
{
public:
struct Entry
{
T data;
};
static CMemoryPool<Entry> s_pool;
};
template<class T>
CList<T>::CMemoryPool<CList<T>::Entry>::s_pool(1);
int main()
{
CList<int> list;
}
I can't seem to get the initialization of s_pool outside of the class to compile. Can anyone help me figure out how to make this work? Note I'm using C++03 only.
I think that you forgot how initializing a static data member works in general:
struct Data { int i; };
struct Holder { static Data StaticMember; };
Data Holder::StaticMember = { 1 };
^ ^~~~~~~~~~~~~~~~~~~~ static member qualified name
\~~~ static member type
If you look at your declaration, it is striking that you forgot one of the two above:
// Only a single element: there should be a type and a name
template<class T>
CList<T>::template CMemoryPool<typename CList<T>::Entry>::s_pool(1);
// Two elements
template<class T>
CMemoryPool<typename CList<T>::Entry> CList<T>::s_pool(1);
^ ^~~~~~~~~~~~~~~~ name
\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ type
Once corrected it just works
EDIT:
I was under the impression that:
You must explicitly give value to the static value for each instantiation of the template:
CList<int>::CMemoryPool<CList<int>::Entry>::s_pool(1);
must be somewhere in your *.C files...
Alternatively, use a static local variable in a method of the table used to get the value.
But after playing a bit, this seems to compile in ideone
template<class T>
CMemoryPool<typename CList<T>::Entry> CList<T>::s_pool(1);
I still recommend #FredOverflow solution as it protects your from static initialization problems
Static data members inside class templates are somewhat of a pain to initialize. I suggest a factory function instead. Then you don't need to worry about defining the variable somewhere else.
Just rewrite the line
static CMemoryPool<Entry> s_pool;
to
static CMemoryPool<Entry>& s_pool()
{
static CMemoryPool<Entry> foobar;
return foobar;
}
And then use s_pool() instead s_pool everywhere. You get lazy initialization as a benefit.
I have a problem regarding 'static const' member initialization. In a templated class I define a const member and initialize it outside the class.
When I include the .h file where this class is implemented in multiple .cpp files, I get an LNK2005 error (I'm using VS2010) that says the constant is already defined.
// List.hpp
template <class T>
class List {
static const double TRIM_THRESHOLD;
};
template <class T>
const double List<T>::TRIM_THRESHOLD = 0.8;
I tried putting the member initialization in a .cpp file, but then I get a linker error saying that the constant is not defined at all. If the list is not templated and I put the initialization in a .cpp file, all is fine.
Is there any solution for this situation? I have #ifdef/define clauses around the file already, and it's definitely not a solution.
You should define the constant in a source file not a header (so it only gets defined once) since this is a template which you need to keep in the header(and all instances have the same value) you can use a common base class.
class ListBase {
protected:
ListBase() {} // use only as base
~ListBase() { } // prevent deletion from outside
static const double TRIM_THRESHOLD;
};
template <class T>
class List : ListBase {
};
// in source file
double ListBase::TRIM_THRESHOLD = 0.8;
Another option is to have it as a static function:
static double trim_threashold() { return 0.8; }
Edit: If your compiler supports C++11 you make your static method a constexpr function so that it has all the optimization opportunities that using the value directly has.