129

I have noticed that some of my functions in a class are actually not accessing the object, so I made them static. Then the compiler told me that all variables they access must also be static – well, quite understandable so far. I have a bunch of string variables such as

string RE_ANY = "([^\\n]*)";
string RE_ANY_RELUCTANT = "([^\\n]*?)";

and so on in the class. I have then made them all static const because they never change. However, my program only compiles if I move them out of the class: Otherwise, MSVC++2010 complains "Only static constant integral variables may be initialized within a class".

Well that's unfortunate. Is there a workaround? I would like to leave them inside the class they belong to.

9 Answers 9

187

They can't be initialised inside the class, but they can be initialised outside the class, in a source file:

// inside the class
class Thing {
    static string RE_ANY;
    static string RE_ANY_RELUCTANT;
};

// in the source file
string Thing::RE_ANY = "([^\\n]*)";
string Thing::RE_ANY_RELUCTANT = "([^\\n]*?)";

Update

I've just noticed the first line of your question - you don't want to make those functions static, you want to make them const. Making them static means that they are no longer associated with an object (so they can't access any non-static members), and making the data static means it will be shared with all objects of this type. This may well not be what you want. Making them const simply means that they can't modify any members, but can still access them.

Sign up to request clarification or add additional context in comments.

10 Comments

They don't access anything in the object, just temporary variables which are given to them as reference arguments. Therefore they probably should be both const and static.
@Felix: it's not possible, const means that it doesn't modify this... and there is no this for static methods.
@androidplusios.design: I don't follow. You need string because it's a variable definition, which needs a type specifier, and the type is string. You can put () around a declarator if you like, it doesn't change the meaning.
@androidplusios.design: It does see that. But you have to use the definition syntax to define and initialise it, and that includes a type specifier, whether or not it's previously been declared. That's how the language is specified.
@androidplusios.design: Or, if you're asking why the syntax is as it is, with all its inconsistencies, redundancies, and ambiguities: because it evolved over many decades from much simpler languages.
|
51

Some answers including even the accepted answer seem to be a little misleading.

You don't have to

  • Always assign a value to static objects when initializing because that's optional.
  • Create another .cpp file for initializing since it can be done in the same header file.

You can even initialize a static object in the same class scope just like a normal variable using the inline keyword.


Initialize with no values in the same file

#include <string>
class A
{
    static std::string str;
    static int x;
};
std::string A::str;
int A::x;

Initialize with values in the same file

#include <string>
class A
{
    static std::string str;
    static int x;
};
std::string A::str = "SO!";
int A::x = 900;

Initialize in the same class scope using the inline keyword

#include <string>
class A
{
    static inline std::string str = "SO!";
    static inline int x = 900;
};

3 Comments

Best answer of them all. I added inline and all of the linking errors disappeard.
'inline variables are a c++ 17 extension'. That's the message I am getting
@Ssenyonjo You've to set the C++ language standard to C++17 or above. i.e in Visual Studio, go Project -> Properties -> C/C++ -> Language -> C++ Language Standard -> ISO C++17 or ISO C++ Latest. And in GCC, use the flag -std=c++17 or -std=c++20 etc.
40

Mike Seymour has given you the right answer, but to add...
C++ lets you declare and define in your class body only static const integral types, as the compiler tells. So you can actually do:

class Foo
{
    static const int someInt = 1;
    static const short someShort = 2;
    // etc.
};

And you can't do that with any other type, in that cases you should define them in your .cpp file.

Comments

28

Since C++11 it can be done inside a class with constexpr.

class stat {
    public:
        // init inside class
        static constexpr double inlineStaticVar = 22;
};

The variable can now be accessed with:

stat::inlineStaticVar

2 Comments

Definitely nice for ints, double, pointers etc. However this doesn't work with the string from the question since string is no literal or reference.
Good point. Though, you could do constexpr char RE_ANY[] = "([^\\n]*)"; or constexpr std::string_view RE_ANY("([^\\n]*)", 9);.
20

Static member variables must be declared in the class and then defined outside of it!

There's no workaround, just put their actual definition in a source file.


From your description it smells like you're not using static variables the right way. If they never change you should use constant variable instead, but your description is too generic to say something more.

Static member variables always hold the same value for any instance of your class: if you change a static variable of one object, it will change also for all the other objects (and in fact you can also access them without an instance of the class - ie: an object).

9 Comments

They are const now -- they just need to be static as well so that I can use them in static member functions. What's the reason for that rule that they must be declared inside and defined outside a class? That doesn't make much sense to me.
@Felix Dombek: I think the reason is that the class is(/could be) declared for each source file you compile and link, but the actual variables must be only defined once. That's the same reason you need to explicitly declare as extern the variables defined in other source files.
@peoro: That seems reasonable! But then why is it allowed for integral datatypes? That shouldn't be allowed either, then ...
@Felix Dombek: it's not standard complaint. ISO C++ forbids in-class initialization of non-const static members. You can only do that for integral const static members, and that's because static const integral variables won't be actually put in memory, but will be used as constant at compile time.
@FelixDombek: To answer your question of why the C++ standard doesn't allow this, see the answer here: stackoverflow.com/questions/9656941/…
|
10

Just to add on top of the other answers. In order to initialize a complex static member, you can do it as follows:

Declare your static member as usual.

// myClass.h
class myClass
{
static complexClass s_complex;
//...
};

Make a small function to initialize your class if it's not trivial to do so. This will be called just the one time the static member is initialized. (Note that the copy constructor of complexClass will be used, so it should be well defined).

//class.cpp    
#include myClass.h
complexClass initFunction()
{
    complexClass c;
    c.add(...);
    c.compute(...);
    c.sort(...);
    // Etc.
    return c;
}

complexClass myClass::s_complex = initFunction();

Comments

9

I feel it is worth adding that a static variable is not the same as a constant variable.

using a constant variable in a class

struct Foo{
    const int a;
    Foo(int b) : a(b){}
}

and we would declare it like like so

fooA = new Foo(5);
fooB = new Foo(10);
// fooA.a = 5;
// fooB.a = 10;

For a static variable

struct Bar{
    static int a;
    Foo(int b){
        a = b;
    }
}
Bar::a = 0; // set value for a

which is used like so

barA = new Bar(5);
barB = new Bar(10);
// barA.a = 10;
// barB.a = 10;
// Bar::a = 10;

You see what happens here. The constant variable, which is instanced along with each instance of Foo, as Foo is instanced has a separate value for each instance of Foo, and it can't be changed by Foo at all.

Where as with Bar, their is only one value for Bar::a no matter how many instances of Bar are made. They all share this value, you can also access it with their being any instances of Bar. The static variable also abides rules for public/private, so you could make it that only instances of Bar can read the value of Bar::a;

3 Comments

huh... find it funny no one has pointed out my terrible needless use of 'new'. Not really of concern for this question, but yeah, avoid using 'new' when you don't need to, and you more or less never do.
Please remove your terrible, needless use of new. ;-)
I feel it now stands as an historic example of how old code was bad. At this point, I think it would be a disservice to remove it.
2

If your goal is to initialize the static variable in your header file (instead of a *.cpp file, which you may want if you are sticking to a "header only" idiom), then you can work around the initialization problem by using a template. Templated static variables can be initialized in a header, without causing multiple symbols to be defined.

See here for an example:

Static member initialization in a class template

Comments

2

Optionally, move all your constants to .cpp file without declaration in .h file. Use anonymous namespace to make them invisible beyond the cpp module.

// MyClass.cpp

#include "MyClass.h"

// anonymous namespace
namespace
{
    string RE_ANY = "([^\\n]*)";
    string RE_ANY_RELUCTANT = "([^\\n]*?)";
}

// member function (static or not)
bool MyClass::foo()
{
    // logic that uses constants
    return RE_ANY_RELUCTANT.size() > 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.