views:

4475

answers:

6

Hi guys.

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. Thanks.

+18  A: 

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

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

// 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.

AndreyT
Also, if there is no requirement for using a STL string, you might as well just define a const char*. (less overhead)
KSchmidt
Tadeusz Kopec
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.
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
+3  A: 

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
+2  A: 

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.

GMan
That looks cool :) - im guessing you have a background in other languages than c++?
lb
Eh, not really. :) I started in VB when i was 8, but I don't use it anymore. I know C pretty well, and of course dabblings in other languages.
GMan
+2  A: 

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.

ltcmelo
+1  A: 

Extra information not really answering your question, but if it wasn't static, you could initialize it in the member initializer of the class in the constructor.

class A
{
public:
   void A() : _s("rectangle")
   {
   }

private:
   const string _s;
};

I'm not 100% sure if this works in the case of static members.

UPDATE: Looks like it doesn't work for static members.

j0rd4n
Static members are classless, no constructor or member variables.
GMan
Ah, okay. Thanks for clarifying. I forgot the rule on that.
j0rd4n
+3  A: 

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