【问题标题】:static const member variable initializationstatic const 成员变量初始化
【发布时间】:2014-09-08 01:01:29
【问题描述】:

看起来我可以初始化一个 POD 静态 const 成员,但不能初始化其他类型:

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

为什么?

【问题讨论】:

  • 我敢打赌,有人会引用标准中的一句话来回答,但这并不能真正告诉你为什么 :-)
  • 我避开了标准。
  • T 认为这是因为它必须在编译时进行评估。这就是为什么它也适用于 C++11 中的 constexpr
  • @Bathsheba 而且您还试图解释为什么,这很好。
  • @juanchopanza:虽然这不是一个完整的答案;绝不是整个故事。我可能会装上它。

标签: c++ class static-members


【解决方案1】:

在 C++17 中:

如果你可以使用static constexpr,你会得到想要的结果,但如果你不能:使用C++17引入的inline variable

在您的情况下,由于std::string 没有constexpr 构造函数,因此解决方案是inline static const std::string

例子:

#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

【讨论】:

    【解决方案2】:

    语法initializer in the class definition 只允许用于整数和枚举类型。对于std::string,必须在类定义之外定义并在那里初始化。

    struct C {
      static const int a = 42;     
      static const string b; 
    };
    
    const string C::b = "hi"; // in one of the .cpp files
    

    static members 必须在一个翻译单元中定义才能满足一个定义规则。如果 C++ 允许下面的定义;

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

    b 将在包含头文件的每个翻译单元中定义。

    C++ 只允许在类声明中定义integralenumeration 类型的const static 数据成员作为快捷方式。无法定义其他类型的const static 数据成员的原因是需要进行非平凡的初始化(需要调用构造函数)。

    【讨论】:

    • 但问题是,为什么?
    【解决方案3】:

    我总结一下C++98 vs C++11上直接类初始化的规则:

    以下代码在 C++03 中是非法的,但在 C++11 中可以正常工作。在 C++11 中,您可以将其视为被注入到 POD 的每个构造函数中的初始化程序,除非该构造函数设置了另一个值。

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

    使字段可变静态成员将破坏两种标准中的代码,这是因为static 保证只有一个变量副本,因此我们必须在一个文件中声明成员,就像我们会使用 extern 关键字来处理全局变量。

    // 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";
    

    如果我们尝试使它们成为 const 静态成员,就会出现一个新的规则数组:

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

    因此,从 C++11 开始,允许将非整数平凡类型的静态常量变为变量。不幸的是,字符串不符合要求,因此即使在 C++11 中我们也无法初始化 constexpr std::strings。

    不过,正如this post 的回答所提到的,您可以创建一个字符串类函数作为字符串文字。

    注意!请注意,这是最好的元编程,如果对象在类中声明为 constexpr static,那么,一旦您进入运行时,就找不到该对象。不知道为什么,欢迎评论。

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

    现在你可以直接在你的类中声明一个 conststr:

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

    【讨论】:

      【解决方案4】:

      string 不是原始类型(如int),而是一个类。

      不允许这样做是明智的; statics 的初始化发生在 main 之前。并且构造函数可以调用初始化时可能不可用的各种函数。

      【讨论】:

      • 这意味着static const char* string = "hi"; 应该可以工作,对吧?
      • 那么为什么可以在函数被调用的时候初始化静态局部变量呢?
      • 这不是真的——你可以像这样在其他范围内初始化常量。它也没有解释像 const char* 这样的其他原语。
      • “构造函数可以调用初始化时可能不可用的各种函数”?这意味着也应该禁止全局对象(即全局非原始变量)(如您所知,情况并非如此)。
      猜你喜欢
      • 2020-09-20
      • 1970-01-01
      • 1970-01-01
      • 2019-09-02
      • 1970-01-01
      • 2013-01-07
      • 1970-01-01
      • 2011-05-18
      相关资源
      最近更新 更多