Static constant string (class member)

435,080

Solution 1

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.

Solution 2

In C++11 you can do now:

class A {
 private:
  static constexpr const char* STRING = "some useful string constant";
};

Solution 3

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.

Solution 4

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*

Solution 5

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.

Share:
435,080
lb.
Author by

lb.

.NET programmer by day. PHP by night. Musician by weekend.

Updated on July 08, 2022

Comments

  • lb.
    lb. almost 2 years

    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.

  • KSchmidt
    KSchmidt over 14 years
    Also, if there is no requirement for using a STL string, you might as well just define a const char*. (less overhead)
  • Tadeusz Kopec for Ukraine
    Tadeusz Kopec for Ukraine over 14 years
    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.
  • Matthieu M.
    Matthieu M. over 14 years
    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...
  • Johannes Schaub - litb
    Johannes Schaub - litb over 14 years
    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.
  • lb.
    lb. over 14 years
    That looks cool :) - im guessing you have a background in other languages than c++?
  • Zoomulator
    Zoomulator almost 12 years
    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.
  • Artur Czajka
    Artur Czajka over 11 years
    Using #define when a typed constant can be used is just wrong.
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com over 11 years
    why does c++ does not allow to initialize inside the class? would it lead to ambiguity or other problems?
  • AnT stands with Russia
    AnT stands with Russia over 11 years
    @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.)
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com over 11 years
    @AndreyT why the exception?
  • AnT stands with Russia
    AnT stands with Russia over 11 years
  • Tebe
    Tebe about 11 years
    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)
  • rank1
    rank1 about 11 years
    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 ?
  • AnT stands with Russia
    AnT stands with Russia about 11 years
    @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.
  • the_mandrill
    the_mandrill over 10 years
    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
  • rwst
    rwst almost 9 years
    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.
  • SmallChess
    SmallChess over 8 years
    Unfortunately this solution doesn't work for std::string.
  • ManuelSchneid3r
    ManuelSchneid3r over 8 years
    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.
  • abyss.7
    abyss.7 over 8 years
    @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.
  • ManuelSchneid3r
    ManuelSchneid3r over 8 years
    When accessing from a friend class clang fails.
  • underscore_d
    underscore_d over 8 years
    @ManuelSchneid3r How exactly is this "not standard conform"? It looks like bog-standard C++11 brace-or-equal initialisation to me.
  • midenok
    midenok almost 8 years
    @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*.
  • Guy Avraham
    Guy Avraham over 6 years
    @abyss.7 - Thanks for your answer, and I have another one please: Why does it have to be static ?
  • Frank Puffer
    Frank Puffer over 6 years
    Your first example is basically a good solution if you don't have constexpr but you cannot make a static function const.
  • Oz Solomon
    Oz Solomon about 6 years
    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; }
  • KRoy
    KRoy almost 6 years
    Don't you think using inline will create a lot of duplicates ?
  • Admin
    Admin almost 6 years
    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
  • zett42
    zett42 over 5 years
  • Admin
    Admin over 5 years
    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.
  • Admin
    Admin over 5 years
    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.
  • Admin
    Admin over 5 years
    This is pre C++11 answer. Use standard C++ and use std::string_view.
  • Lukas Salich
    Lukas Salich about 5 years
    C++11 does not have std::string_view.
  • bweber13
    bweber13 about 4 years
    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
    bweber13 about 4 years
    It was because I was trying to do it inside a member function, moving it outside the function fixed the problem.
  • Thomas Liao
    Thomas Liao over 3 years
    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
  • Serikov
    Serikov almost 2 years
    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
  • AnT stands with Russia
    AnT stands with Russia almost 2 years
    @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.