112

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.

jww
  • 97,681
  • 90
  • 411
  • 885
Felix Dombek
  • 13,664
  • 17
  • 79
  • 131

9 Answers9

166

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.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 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`. – Felix Dombek Feb 16 '11 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. – Matthieu M. Feb 16 '11 at 17:58
  • @Matthieu: Very good explanation. So `static` is the right one. – Felix Dombek Feb 16 '11 at 17:59
  • @Felix: OK, if they don't access the object at all, then they should be static. That's not what the question says, though. – Mike Seymour Feb 16 '11 at 18:04
  • I think, there is still a bit of confusion about `static` and `const` here: The OP said, that he has `static` functions, that use `static const` values. So his values are both `static` AND `const`, and that is clearly the case, where initialization at compile time is most important. – Kai Petzke May 10 '14 at 09:29
  • @MikeSeymour why to use `string` in front of Thing::RE_ANY` i noticed that we can also do string(Thing::RE_ANY) and it works....why? – abhimanyuaryan Mar 23 '15 at 19:34
  • 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. – Mike Seymour Mar 23 '15 at 19:35
  • 1
    @MikeSeymour so is it the definition not exactly the assignment. Why the compiler doesn't see that we have already declared a string named `RE_ANY` and now we want to initialize it? – abhimanyuaryan Mar 23 '15 at 19:42
  • 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. – Mike Seymour Mar 23 '15 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. – Mike Seymour Mar 23 '15 at 19:46
37

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.

Santiago V.
  • 1,088
  • 8
  • 24
31

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;
};
Beyondo
  • 2,952
  • 1
  • 17
  • 42
  • 6
    Best answer of them all. I added `inline` and all of the linking errors disappeard. – Contango Mar 21 '20 at 13:37
  • 1
    'inline variables are a c++ 17 extension'. That's the message I am getting – Gilbert May 26 '20 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 May 26 '20 at 19:30
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
0ax1
  • 475
  • 3
  • 11
  • 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. – Roi Danton Apr 18 '17 at 10:59
  • 1
    Good point. Though, you could do `constexpr char RE_ANY[] = "([^\\n]*)";` or `constexpr std::string_view RE_ANY("([^\\n]*)", 9);`. – 0ax1 Apr 19 '17 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).

peoro
  • 25,562
  • 20
  • 98
  • 150
  • 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. – Felix Dombek Feb 16 '11 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 Feb 16 '11 at 17:40
  • 2
    @peoro: That seems reasonable! But then why is it allowed for integral datatypes? That shouldn't be allowed either, then ... – Felix Dombek Feb 16 '11 at 17:56
  • 1
    @Felix Dombek: it's not allowed for integral types too. If you define this struct: `struct A { static int x; };`, and you try to access `A::x` without defining `int A::x;` in a source file, your code won't link. – peoro Feb 16 '11 at 18:07
  • @peoro: MSVC says that I could write `struct A { static int x = 5; };` and the in-class definition would then be legal. – Felix Dombek Feb 16 '11 at 18:42
  • 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 Feb 16 '11 at 18:45
  • @Felix Dombek: I wonder what happens if you try to write on `A::x` (`A::x=0;`). Will MSVC compile? – peoro Feb 16 '11 at 18:47
  • @peoro: my bad, i meant `static const int x = 5` – Felix Dombek Feb 16 '11 at 21:41
  • 2
    @FelixDombek: To answer your question of *why* the C++ standard doesn't allow this, see the answer here: http://stackoverflow.com/questions/9656941/why-i-cant-initialize-non-const-static-member-or-static-array-in-class – Brian Stormont Nov 08 '12 at 20:30
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();
Sergio Basurco
  • 3,488
  • 2
  • 22
  • 40
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;

thecoshman
  • 8,394
  • 8
  • 55
  • 77
  • 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 Nov 01 '14 at 15:25
  • Please remove your terrible, needless use of `new`. ;-) – Roi Danton Sep 13 '18 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 Oct 01 '18 at 17:05
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

Community
  • 1
  • 1
Mark Lakata
  • 19,989
  • 5
  • 106
  • 123
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;
}
liquid_code
  • 577
  • 7
  • 12