121

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

176

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.

10
  • 2
    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. Commented Feb 16, 2011 at 17:52
  • 8
    @Felix: it's not possible, const means that it doesn't modify this... and there is no this for static methods. Commented Feb 16, 2011 at 17:58
  • 2
    @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. Commented Mar 23, 2015 at 19:35
  • 4
    @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. Commented Mar 23, 2015 at 19:44
  • 4
    @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. Commented Mar 23, 2015 at 19:46
46

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
  • 9
    Best answer of them all. I added inline and all of the linking errors disappeard.
    – Contango
    Commented Mar 21, 2020 at 13:37
  • 4
    'inline variables are a c++ 17 extension'. That's the message I am getting
    – Gilbert
    Commented May 26, 2020 at 18:25
  • 1
    @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.
    – Beyondo
    Commented May 26, 2020 at 19:30
39

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.

24

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
  • 2
    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.
    – Richard
    Commented Apr 18, 2017 at 10:59
  • 2
    Good point. Though, you could do constexpr char RE_ANY[] = "([^\\n]*)"; or constexpr std::string_view RE_ANY("([^\\n]*)", 9);.
    – 0ax1
    Commented Apr 19, 2017 at 20:12
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
  • 4
    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. Commented Feb 16, 2011 at 17:37
  • 3
    @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
    Commented Feb 16, 2011 at 17:40
  • 2
    @peoro: That seems reasonable! But then why is it allowed for integral datatypes? That shouldn't be allowed either, then ... Commented Feb 16, 2011 at 17:56
  • 3
    @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.
    – peoro
    Commented Feb 16, 2011 at 18:45
  • 2
    @FelixDombek: To answer your question of why the C++ standard doesn't allow this, see the answer here: stackoverflow.com/questions/9656941/… Commented Nov 8, 2012 at 20:30
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
  • 1
    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.
    – thecoshman
    Commented Nov 1, 2014 at 15:25
  • Please remove your terrible, needless use of new. ;-)
    – Richard
    Commented Sep 13, 2018 at 13:06
  • 1
    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.
    – thecoshman
    Commented Oct 1, 2018 at 17:05
9

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();
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

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;
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.