static const member variable initialization
Asked Answered
S

4

11

Looks like I can init a POD static const member, but not other types:

struct C {
  static const int a = 42;      // OK
  static const string b = "hi"; // compile error
};

Why?

Sadoff answered 17/7, 2014 at 8:42 Comment(6)
I bet someone will answer with a quote from the standard, which doesn't really tell you why :-)Fixative
I avoided the standard.Unstring
T think it's because it hase to be evaluatet at compile-time. Thats why it also works with constexpr in C++11Shawn
@Unstring And you're also attempting to explain why, which is good.Fixative
@juanchopanza: it's not a full answer though; by no means the whole story. I might tin it.Unstring
Possible duplicate of C++ where to initialize static constInharmonic
G
8

The syntax initializer in the class definition is only allowed with integral and enum types. For std::string, it must be defined outside the class definition and initialized there.

struct C {
  static const int a = 42;     
  static const string b; 
};

const string C::b = "hi"; // in one of the .cpp files

static members must be defined in one translation unit to fulfil the one definition rule. If C++ allows the definition below;

struct C {
  static const string b = "hi"; 
};

b would be defined in each translation unit that includes the header file.

C++ only allows to define const static data members of integral or enumeration type in the class declaration as a short-cut. The reason why const static data members of other types cannot be defined is that non-trivial initialization would be required (constructor needs to be called).

Gemology answered 17/7, 2014 at 8:48 Comment(1)
But the question is, why?Fixative
U
6

string is not a primitive type (like int) but is a class.

Disallowing this is sensible; the initialisation of statics happens before main. And constructors can invoke all sorts of functions that might not be available on initialisation.

Unstring answered 17/7, 2014 at 8:44 Comment(4)
that means static const char* string = "hi"; should work, right?Lifeline
So why is it possible to initialize static locals in a function at the moment the function is called?Fixative
That's not true- you can initialize constants at other scopes like this. It also doesn't explain other primitives like const char*.Exciter
"Constructors can invoke all sorts of functions that might not be available on initialization"? This implies that global objects (i.e., global non-primitive variables) should also be disallowed (which, as you know, is not the case).Counterproductive
F
5

I'll sum up the rules about direct class initialization on C++98 vs C++11:

The following code is illegal in C++03, but works just as you expect in C++11. In C++11, you can think of it as the initializers being injected into each of the constructors of POD, unless that constructor sets another value.

struct POD {
    int integer = 42; 
    float floating_point = 4.5f;
    std::string character_string = "Hello";
};

Making the fields mutable static members will break the code in both standards, this is because the static guarantees there to be only one copy of the variable and thus we have to declare the members in a exactly one file, just like we would do with global variables using the referred using the extern keyword.

// This does not work
struct POD {
    static int integer = 42;
    static float floating_point = 4.5f;
    static std::string character_string = "Hello";
};

int   POD::integer = 42;
float POD::floating_point = 4.5f;
std::string POD::character_string = "Hello";

// This works
struct POD {
    static int integer;
    static float floating_point;
    static std::string character_string;
};

int   POD::integer = 42;
float POD::floating_point = 4.3f;
std::string POD::character_string = "hello";

If we try to make them, const static members a new array of rules arise:

struct POD {
    static const int integer = 42;               // Always works
    static constexpr float floating_point = 4.5f;    // Works in C++11 only.
    static const std::string character_string = "Hello"; // Does not work.
    constexpr static const std::string character_string = "Hello"; // Does not work (last checked in C++11)

    // Like some others have also mentioned, this works.
    static const std::string character_string;
};

// In a sourcefile:
const std::string POD::character_string = "Hello";

So, from C++11 onwards, it is allowed to make static constants of non-integer trivial types variable. Strings unfortunately does not fit the bill, therefore we cannot initialize constexpr std::strings even in C++11.

All is not lost though, as an answer to this post mentions, you could create a string class functions as a string literal.

NB! Note that you this is metaprogramming at its best, if the object is declared as constexpr static inside a class, then, as soon as you enter run-time, the object is nowhere to be found. I haven't figured out why, please feel free to comment on it.

// literal string class, adapted from: http://en.cppreference.com/w/cpp/language/constexpr
class conststr {
    const char * p;
    std::size_t sz; 
    public:
    template<std::size_t N>
        constexpr conststr(const char(&a)[N]) : p(a), sz(N-1) {}
    // constexpr functions signal errors by throwing exceptions from operator ?:
    constexpr char operator[](std::size_t n) const {
        return n < sz ? p[n] : throw std::out_of_range("");
    }   
    constexpr std::size_t size() const { return sz; }

    constexpr bool operator==(conststr rhs) {
        return compare(rhs) == 0;
    }   

    constexpr int compare(conststr rhs, int pos = 0) {
        return ( this->size() < rhs.size() ? -1 :
                    ( this->size() > rhs.size() ? 1 : 
                        ( pos == this->size()  ? 0 : 
                            ( (*this)[pos] < rhs[pos] ? -1 :
                                ( (*this)[pos] > rhs[pos] ? 1 : 
                                    compare(rhs, pos+1)
                                )   
                            )   
                        )   
                    )   
                );  
    }   

    constexpr const char * c_str() const { return p; }
};

Now you can declare a conststr directly in you class:

struct POD {
    static const int integer = 42;               // Always works
    static constexpr float floating_point = 4.5f;          // Works in C++11 only.
    static constexpr conststr character_string = "Hello"; // C++11 only, must be declared.
};  



int main() {
    POD pod; 

    // Demonstrating properties.
    constexpr conststr val = "Hello";
    static_assert(val == "Hello", "Ok, you don't see this.");
    static_assert(POD::character_string == val, "Ok");
    //static_assert(POD::character_string == "Hi", "Not ok.");
    //static_assert(POD::character_string == "hello", "Not ok.");

    constexpr int compare = val.compare("Hello");
    cout << compare << endl;

    const char * ch = val.c_str(); // OK, val.c_str() is substituted at compile time.
    cout << ch << endl;   // OK

    cout << val.c_str() << endl; // Ok

    // Now a tricky one, I haven't figured out why this one does not work:
    // cout << POD::character_string.c_str() << endl; // This fails linking.

    // This works just fine.
    constexpr conststr temp = POD::character_string;
    cout << temp.c_str() << endl;
} 
Frizzle answered 17/7, 2014 at 10:29 Comment(0)
T
1

In C++17 :

If you can use static constexpr you will have the desired result, but if you cant : use the inline variable introduce by C++17.

In your case, since std::string does not have a constexpr constructor, the solution is inline static const std::string

Example :

#include <iostream>

int foo() { return 4;}

struct Demo
{
   inline static const int i = foo();  
   inline static const std::string str = "info";  

    
};
int main() {
    std::cout << Demo::i << " " << Demo::str<< std::endl;
    
}

Live Demo

Termination answered 6/7, 2021 at 8:7 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.