【问题标题】:Static field initialization order静态字段初始化顺序
【发布时间】:2014-05-24 02:07:53
【问题描述】:

静态常量字段初始化顺序有什么陷阱吗?

template <typename T>
struct constant_test {

    static const T PI;
    static const T FULL_CIRCLE;
    static const T HALF_CIRCLE;
    static const T DEG_TO_RAD;

};

template <typename T> const T constant_test<T>::PI = 3.141592653589f;
template <typename T> const T constant_test<T>::FULL_CIRCLE = 360.0f;
template <typename T> const T constant_test<T>::HALF_CIRCLE = constant_test<T>::FULL_CIRCLE / 2;
template <typename T> const T constant_test<T>::DEG_TO_RAD = constant_test<T>::PI / constant_test<T>::HALF_CIRCLE;

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
    // uncomment to make it work
    // float test_ref = constant_test<float>::HALF_CIRCLE;
    char buf[128];
    sprintf_s(buf, 128, "Value: %f", constant_test<float>::DEG_TO_RAD);
    OutputDebugStringA(buf); // prints "Value: 1.#INF00"
    return 0;
}

表达式constant_test&lt;float&gt;::DEG_TO_RAD 神奇地返回-Infinity

如果我删除模板参数并使其仅浮动,则正确评估常量 (0.017453)

如果我添加对 HALF_CIRCLE 常量的引用,那么它也会被正确评估

我正在使用 MSVC 2013 SP 1。

为什么? 我错过了什么?

【问题讨论】:

    标签: c++ windows visual-c++


    【解决方案1】:

    根据

    初始化有以下几种:

    • 静态初始化
      • 零初始化:例如全局范围内的int x;
      • 常量初始化:例如int x=1+2; 其中1+2 是常量表达式
    • 动态初始化
      • 有序初始化:例如
        • std::vector&lt;int&gt; x{1, 2, 3}; 在全局范围内,或
        • template&lt;&gt; std::vector&lt;int&gt; A&lt;int&gt;::x{1, 2, 3};(明确专业化)
      • 无序初始化:例如template&lt;class T&gt; std::vector&lt;T&gt; A&lt;T&gt;::x;

    请参阅 What is Unordered dynamic initialization, Partially-ordered dynamic initialization and Ordered dynamic initialization 了解这些术语的含义。

    在这种情况下

    template <typename T> const T constant_test<T>::PI = 3.141592653589f;
    template <typename T> const T constant_test<T>::FULL_CIRCLE = 360.0f;
    

    是常量初始化,因为在这种情况下它们是浮点类型,所以它们不是 constexpr,根据c++ - Initializing constexpr with const: Different treatment for int and double - Stack Overflow

    template <typename T> const T constant_test<T>::HALF_CIRCLE = constant_test<T>::FULL_CIRCLE / 2;
    template <typename T> const T constant_test<T>::DEG_TO_RAD = constant_test<T>::PI / constant_test<T>::HALF_CIRCLE;
    

    是动态无序初始化。

    因此,不保证最后2个的初始化顺序。

    【讨论】:

    • 可能相关:它们只是不是常量初始化的,因为T 是浮点类型。对于整数类型,它们将是。
    • const 由常量表达式初始化的整型变量在表达式中使用时的行为就好像它们是constexpr。这是使用变量为constexpr 的要求的特定例外。
    【解决方案2】:

    当代码转换为控制台应用程序并使用 g++ 时,它会打印出正确的值(g++4.8.2,OS X):

    #include <iostream>
    
    using namespace std;
    
    template <typename T>
    struct constant_test {
    
        static const T PI;
        static const T FULL_CIRCLE;
        static const T HALF_CIRCLE;
        static const T DEG_TO_RAD;
    
    };
    
    template <typename T> const T constant_test<T>::PI = 3.141592653589f;
    template <typename T> const T constant_test<T>::FULL_CIRCLE = 360.0f;
    template <typename T> const T constant_test<T>::HALF_CIRCLE = constant_test<T>::FULL_CIRCLE / 2;
    template <typename T> const T constant_test<T>::DEG_TO_RAD = constant_test<T>::PI / constant_test<T>::HALF_CIRCLE;
    
    int main()
    {
        // uncomment to make it work
        //float test_ref = constant_test<float>::HALF_CIRCLE;
        cout << "Value: " << constant_test<float>::DEG_TO_RAD << endl; // correct result
    
        char buf[128];
        sprintf(buf, "Value: %f", constant_test<float>::DEG_TO_RAD );
        cout << buf << endl; // again correct
        
        return 0;
    }
    

    输出:

    Value: 0.0174533
    Value: 0.017453
    

    静态常量按顺序初始化,请参阅https://stackoverflow.com/a/10011133/3093378,因此您应该在显示constant_test&lt;T&gt;::DEG_TO_RAD 时对其进行初始化(从上面的代码看起来确实如此)。

    【讨论】:

    • 你认为..微软吗?
    • 您是否尝试在类似控制台的应用程序中运行上述代码?
    • 如果我使用典型的sprintf 作为sprintf(buf, "Value: %f", constant_test&lt;float&gt;::DEG_TO_RAD );,然后cout &lt;&lt; buf &lt;&lt; endl;,我仍然得到相同的正确结果
    • 直接复制粘贴您的版本。 cmd.exe: constant_test\Debug&gt;constant_test.exe Value: 1.#INF... =) 我不确定是否允许从同一编译单元中的另一个静态字段引用静态字段。但为什么不......看起来像一个错误
    • 不保证相关成员的初始化顺序正确。查看其他答案。
    猜你喜欢
    • 1970-01-01
    • 2010-12-02
    • 2017-05-31
    • 2015-05-19
    • 2014-10-03
    • 1970-01-01
    • 2021-07-12
    • 1970-01-01
    相关资源
    最近更新 更多