524

I'd like to have a private static constant for a class (in this case a shape-factory).

I'd like to have something of the sort.

class A {
   private:
      static const string RECTANGLE = "rectangle";
}

Unfortunately I get all sorts of error from the C++ (g++) compiler, such as:

ISO C++ forbids initialization of member ‘RECTANGLE’

invalid in-class initialization of static data member of non-integral type ‘std::string’

error: making ‘RECTANGLE’ static

This tells me that this sort of member design is not compliant with the standard. How do you have a private literal constant (or perhaps public) without having to use a #define directive (I want to avoid the uglyness of data globality!)

Any help is appreciated.

Community
  • 1
  • 1
lb.
  • 5,666
  • 3
  • 17
  • 16
  • Can someone please tell me what an 'integral' type is? Thank you very much. – lb. Oct 16 '09 at 13:43
  • 1
    Integral types refers to types that represent integer numbers. See http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fintvar.htm – bleater Jul 27 '12 at 04:37
  • Private static string in your factory is not good solution - consider that your factory clients will have to know what shapes are supported, so instead of keeping it in private static, put them into separate namespace as static const std::string RECTANGLE = "Rectangle". – LukeCodeBaker Jan 09 '16 at 22:15
  • if your class is a template class then see https://stackoverflow.com/q/3229883/52074 – Trevor Boyd Smith Jul 09 '18 at 12:50

11 Answers11

589

You have to define your static member outside the class definition and provide the initializer there.

First

// In a header file (if it is in a header file in your case)
class A {   
private:      
  static const string RECTANGLE;
};

and then

// In one of the implementation files
const string A::RECTANGLE = "rectangle";

The syntax you were originally trying to use (initializer inside class definition) is only allowed with integral and enum types.


Starting from C++17 you have another option, which is quite similar to your original declaration: inline variables

// In a header file (if it is in a header file in your case)
class A {   
private:      
  inline static const string RECTANGLE = "rectangle";
};

No additional definition is needed.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 10
    Also, if there is no requirement for using a STL string, you might as well just define a const char*. (less overhead) – KSchmidt Oct 14 '09 at 02:23
  • 55
    I'm not sure it's always less overhead - it depends on usage. If this member is meant to be passed as an argument to functions that take const string &, there will be temporary created for each call vs one string object creation during initialization. IMHO overhead for creating a static string object is neglible. – Tadeusz Kopec for Ukraine Oct 14 '09 at 12:49
  • 26
    I'd rather use std::string's all the time too. The overhead is negligible, but you have far more options and are much less likely to write some fool things like "magic" == A::RECTANGLE only to compare their address... – Matthieu M. Oct 14 '09 at 14:24
  • 9
    the `char const*` has the goodness that it is initialized before all dynamic initialization is done. So in any object's constructor, you can rely on `RECTANGLE` to have been initialized alreay then. – Johannes Schaub - litb Oct 16 '09 at 11:40
  • 2
    why does c++ does not allow to initialize inside the class? would it lead to ambiguity or other problems? – Ciro Santilli OurBigBook.com Jan 10 '13 at 14:30
  • 9
    @cirosantilli: Because from the beginning of times in C++ initializers were parts of *definitions*, not *declarations*. And data member declaration inside the class is just that: a declaration. (On the other hand, an exception was made for const integral and enum members, and in C++11 - for const members of *literal* types.) – AnT stands with Russia Jan 10 '13 at 16:24
  • @AndreyT why the exception? – Ciro Santilli OurBigBook.com Jan 10 '13 at 16:31
  • 2
    @cirosantilli: http://stackoverflow.com/questions/13697265/static-const-double-cannot-have-an-in-class-initializer-why-is-it-so – AnT stands with Russia Jan 10 '13 at 17:34
  • what's about const char[], isn't it better than const char *? (AFAIK in the case of const char[] there is no need to hold pointer, name is just associated with array) – Tebe Mar 25 '13 at 18:28
  • Ok, so let there be class Rectangle and Traingle and Square that inherits from it. In which class should I put strsng Rectangle definition when I don't know which class object (triangle or square) will be created first ? – rank1 Apr 15 '13 at 08:49
  • @Sebastian Cygert: I'm not sure I understand the question. It doesn't matter what will be created first or what inherits from what. If the static member is *declared* as a member of class `A`, it has to be *defined* later as member of class `A`. – AnT stands with Russia Apr 16 '13 at 18:50
  • Surely static strings are not a Good Thing as they suffer from order-of-initialisation problems, so you may get a crash if called from another static. That's one reason to prefer either a `char*` or a static _method_ instead – the_mandrill Sep 23 '13 at 11:24
  • trying to use `const string A::RECTANGLE = "rectangle";` in the implementation gives me the error `qualified-id in declaration before ‘=’ token` in g++ 7.4.0 – bweber13 Mar 02 '20 at 21:13
  • It was because I was trying to do it inside a member function, moving it outside the function fixed the problem. – bweber13 Mar 02 '20 at 21:30
  • 1
    in the first solution, it comes the warning: Clang-Tidy: Initialization of 'RECTANGLE' with static storage duration may throw an exception that cannot be caught – Thomas Liao Jan 22 '21 at 03:25
  • 3
    *Starting from C++20 instead of const you can declare it constexpr* - No. `std::string` can't be `constexpr` in C++20. It can be used inside `constexpr` functions but not as `constexpr` variable. See [link to godbolt](https://gcc.godbolt.org/z/Gf96hsz8d) – Serikov Jun 14 '22 at 18:18
  • 1
    @Serikov: Yes, you are right. It is possible to have a nameless temporary of `std::string` type in a constant expression as well as a local variable inside a `constexpr` function, but not a long-lived/named `constexpr std::string` object. I edited the answer. – AnT stands with Russia Jun 15 '22 at 19:17
179

In C++11 you can do now:

class A {
 private:
  static constexpr const char* STRING = "some useful string constant";
};
abyss.7
  • 13,882
  • 11
  • 56
  • 100
  • Thanks, that saved the day. I needed a bitset of size of a static array in my class. Not possible without constexpr (for the array) unless you want heap allocation every time the class is created. – rwst Aug 04 '15 at 06:33
  • 36
    Unfortunately this solution doesn't work for std::string. – ABCD Oct 27 '15 at 05:51
  • 3
    Note that 1. this only works with literals and 2. this is not standard conform, although Gnu/GCC compliles fines, other compilers will throw an error. Definition has to be in body. – ManuelSchneid3r Nov 16 '15 at 11:40
  • @ManuelSchneid3r what other compilers do you mean? - At least Clang compiles fine too. Also the question is about string literal and doesn't mention otherwise. – abyss.7 Nov 17 '15 at 10:49
  • When accessing from a friend class clang fails. – ManuelSchneid3r Nov 17 '15 at 11:34
  • 2
    @ManuelSchneid3r How exactly is this "not standard conform"? It looks like bog-standard C++11 _brace-or-equal initialisation_ to me. – underscore_d Feb 10 '16 at 11:24
  • 5
    @rvighne, no that's incorrect. `constexpr` implies `const` for var, not for type it points. I.e. `static constexpr const char* const` is the same as `static constexpr const char*`, but not the same as `static constexpr char*`. – midenok Aug 11 '16 at 14:46
  • 2
    @abyss.7 - Thanks for your answer, and I have another one please: Why does it have to be static ? – Guy Avraham Nov 06 '17 at 06:55
  • 1
    This is not modern C++. Because it is a "naked" pointer and second because it is essentially dangerous. It will be placed in R/O memory. Any accidental free() or delete on this will crash the app. And believe you me, people will do exactly that. The remedy is to use std::string_view literal. –  Oct 25 '18 at 07:53
  • This does not work for a more complicated struct...
    class A {
         typedef struct {
            int a;
            const char* b;
         } foo;
         private:
          static constexpr foo= { a, "string" };
        };
    
    – brookbot Jun 08 '23 at 21:33
34

Inside class definitions you can only declare static members. They have to be defined outside of the class. For compile-time integral constants the standard makes the exception that you can "initialize" members. It's still not a definition, though. Taking the address would not work without definition, for example.

I'd like to mention that I don't see the benefit of using std::string over const char[] for constants. std::string is nice and all but it requires dynamic initialization. So, if you write something like

const std::string foo = "hello";

at namespace scope the constructor of foo will be run right before execution of main starts and this constructor will create a copy of the constant "hello" in the heap memory. Unless you really need RECTANGLE to be a std::string you could just as well write

// class definition with incomplete static member could be in a header file
class A {
    static const char RECTANGLE[];
};

// this needs to be placed in a single translation unit only
const char A::RECTANGLE[] = "rectangle";

There! No heap allocation, no copying, no dynamic initialization.

Cheers, s.

sellibitze
  • 27,611
  • 3
  • 75
  • 95
22

In C++ 17 you can use inline variables:

class A {
 private:
  static inline const std::string my_string = "some useful string constant";
};

Note that this is different from abyss.7's answer: This one defines an actual std::string object, not a const char*

Oz Solomon
  • 2,969
  • 23
  • 22
17

This is just extra information, but if you really want the string in a header file, try something like:

class foo
{
public:
    static const std::string& RECTANGLE(void)
    {
        static const std::string str = "rectangle";

        return str;
    }
};

Though I doubt that's recommended.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • That looks cool :) - im guessing you have a background in other languages than c++? – lb. Oct 16 '09 at 13:41
  • 5
    I wouldn't not recommend it. I do this frequently. It works fine and I find it more obvious than putting the string in the implementation file. The actual data of std::string is still located on heap though. I'd return a const char*, in which case you don't need to declare the static variable so the declaration would take less space (code wise). Just a matter of taste though. – Zoomulator Jun 18 '12 at 18:27
11

The class static variables can be declared in the header but must be defined in a .cpp file. This is because there can be only one instance of a static variable and the compiler can't decide in which generated object file to put it so you have to make the decision, instead.

To keep the definition of a static value with the declaration in C++11 a nested static structure can be used. In this case the static member is a structure and has to be defined in a .cpp file, but the values are in the header.

class A
{
private:
  static struct _Shapes {
     const std::string RECTANGLE {"rectangle"};
     const std::string CIRCLE {"circle"};
  } shape;
};

Instead of initializing individual members the whole static structure is initialized in .cpp:

A::_Shapes A::shape;

The values are accessed with

A::shape.RECTANGLE;

or -- since the members are private and are meant to be used only from A -- with

shape.RECTANGLE;

Note that this solution still suffers from the problem of the order of initialization of the static variables. When a static value is used to initialize another static variable, the first may not be initialized, yet.

// file.h
class File {
public:
  static struct _Extensions {
    const std::string h{ ".h" };
    const std::string hpp{ ".hpp" };
    const std::string c{ ".c" };
    const std::string cpp{ ".cpp" };
  } extension;
};

// file.cpp
File::_Extensions File::extension;

// module.cpp
static std::set<std::string> headers{ File::extension.h, File::extension.hpp };

In this case the static variable headers will contain either { "" } or { ".h", ".hpp" }, depending on the order of initialization created by the linker.

As mentioned by @abyss.7 you could also use constexpr if the value of the variable can be computed at compile time. But if you declare your strings with static constexpr const char* and your program uses std::string otherwise there will be an overhead because a new std::string object will be created every time you use such a constant:

class A {
public:
   static constexpr const char* STRING = "some value";
};
void foo(const std::string& bar);
int main() {
   foo(A::STRING); // a new std::string is constructed and destroyed.
}
Marko Mahnič
  • 715
  • 6
  • 11
  • Well prepared answer Marko. Two details: one does not need cpp files for static class members, and also please use std::string_view for any kind of constants. –  Oct 25 '18 at 07:46
8

To use that in-class initialization syntax, the constant must be a static const of integral or enumeration type initialized by a constant expression.

This is the restriction. Hence, in this case you need to define variable outside the class. refer answwer from @AndreyT

aJ.
  • 34,624
  • 22
  • 86
  • 128
5

You can either go for the const char* solution mentioned above, but then if you need string all the time, you're going to have a lot of overhead.
On the other hand, static string needs dynamic initialization, thus if you want to use its value during another global/static variable's initialization, you might hit the problem of initialization order. To avoid that, the cheapest thing is accessing the static string object through a getter, which checks if your object is initialized or not.

//in a header  
class A{  
  static string s;   
public:   
  static string getS();  
};  
//in implementation  
string A::s;  
namespace{  
  bool init_A_s(){  
    A::s = string("foo");   
    return true;  
  }  
  bool A_s_initialized = init_A_s();  
}  
string A::getS(){      
  if (!A_s_initialized)  
    A_s_initialized = init_A_s();  
  return s;  
}  

Remember to only use A::getS(). Because any threading can only started by main(), and A_s_initialized is initialized before main(), you don't need locks even in a multithreaded environment. A_s_initialized is 0 by default (before the dynamic initialization), so if you use getS() before s is initialized, you call the init function safely.

Btw, in the answer above: "static const std::string RECTANGLE() const" , static functions cannot be const because they cannot change the state if any object anyway (there is no this pointer).

Jost
  • 1,549
  • 12
  • 18
user2806882
  • 81
  • 1
  • 4
5

possible just do:

static const std::string RECTANGLE() const {
    return "rectangle";
} 

or

#define RECTANGLE "rectangle"
RiaD
  • 46,822
  • 11
  • 79
  • 123
chikuba
  • 4,229
  • 6
  • 43
  • 75
  • 11
    Using #define when a typed constant can be used is just wrong. – Artur Czajka Oct 16 '12 at 08:10
  • Your first example is basically a good solution if you don't have `constexpr` but you cannot make a static function `const`. – Frank Puffer Jan 25 '18 at 09:29
  • This solution should be avoided. It creates a new string on every invocation. This would be better: `static const std::string RECTANGLE() const { static const std::string value("rectangle"); return value; }` – Oz Solomon Mar 02 '18 at 14:56
  • Why using the full-blown container as a return value? Use std::string_vew .. it's content will stay valid in this case. even better use string literals to make and return the string view ... and last but not the least, const return value has no meaning or effect here ..ah yes, and have this as inline, not a static, in some header in named namespace ... and please make it to be constexpr –  Aug 19 '18 at 09:21
4

The current standard only allows such initialization for static constant integral types. So you need to do as AndreyT explained. However, that will be available in the next standard through the new member initialization syntax.

Leandro T. C. Melo
  • 3,974
  • 22
  • 22
4

Fast forward to 2018 and C++17.

  • do not use std::string, use std::string_view literals
  • please do notice the 'constexpr' bellow. This is also an "compile time" mechanism.
  • no inline does not mean repetition
  • no cpp files are not necessary for this
  • static_assert 'works' at compile time only

    using namespace std::literals;
    
    namespace STANDARD {
    constexpr 
    inline 
    auto 
    compiletime_static_string_view_constant() {
    // make and return string view literal
    // will stay the same for the whole application lifetime
    // will exhibit standard and expected interface
    // will be usable at both
    // runtime and compile time
    // by value semantics implemented for you
        auto when_needed_ =  "compile time"sv;
        return when_needed_  ;
    }
    

    };

Above is a proper and legal standard C++ citizen. It can get readily involved in any and all std:: algorithms, containers, utilities and a such. For example:

// test the resilience
auto return_by_val = []() {
    auto return_by_val = []() {
        auto return_by_val = []() {
            auto return_by_val = []() {
return STANDARD::compiletime_static_string_view_constant();
            };
            return return_by_val();
        };
        return return_by_val();
    };
    return return_by_val();
};

// actually a run time 
_ASSERTE(return_by_val() == "compile time");

// compile time 
static_assert(
   STANDARD::compiletime_static_string_view_constant() 
   == "compile time" 
 );

Enjoy the standard C++

  • 1
    Use `std::string_view` for constants only if you use `string_view` parameters in all your functions. If any of your functions uses a `const std::string&` parameter, a copy of a string will be created when you pass a `string_view` constant through that parameter. If your constants are of type `std::string` the copies will not be created neither for `const std::string&` parameters nor for `std::string_view` parameters. – Marko Mahnič Aug 23 '19 at 09:22
  • Nice answer, but curious as to why the string_view is being returned from a function? This sort of trick was useful before `inline` variables arrived in C++17 with their ODR semantics. But string_view is C++17 too, so just `constexpr auto some_str = "compile time"sv;` does the job (and actually, it's not a variable, it's `constexpr`, so `inline` is implicit; if you have a variable - i.e. no `constexpr` - then `inline auto some_str = "compile time"sv;` will do it, though of course a namespace-scope variable, which is essentially a global variable, would rarely be a good idea). – Loss Mentality Apr 11 '20 at 00:48