static const member variable initialization

冷暖自知 提交于 2021-02-18 05:36:50

问题


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?


回答1:


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




回答2:


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.




回答3:


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;
} 


来源:https://stackoverflow.com/questions/24798761/static-const-member-variable-initialization

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!