为什么 static const char * template struct 成员没有初始化

Why is static const char * template struct member not initialized

我有一些 C++11 模板代码,我正在尝试移植到 Visual C++ Compiler 2015。原始代码工作得很好,但是我需要重写它以解决 constexpr 的问题。

The original code (simplified example)

#include <iostream>

struct String
{
    static constexpr const char * value{ "STRING" };
};

template<typename Base>
class Derived
{
public:
    static constexpr const char * value{ Base::value };
};

template<typename BarType>
struct Foo
{
    static constexpr const char * value{ BarType::value };
};

using Bar = Derived<String>;
using FooBar = Foo<Bar>;

int main()
{
    std::cout << "FooBar::value = " << FooBar::value << std::endl;
}

这会打印:

FooBar::value = STRING

但是,当我重写时,一些静态变量没有被初始化。即使它编译得很好。

The ported code (not working)

#include <iostream>

struct String
{
    static const char * value;
};
const char * String::value = "STRING";

template<typename Base>
class Derived
{
public:
    static const char * value;
};
template<typename Base>
const char * Derived<Base>::value = { Base::value };

template<typename BarType>
struct Foo
{
    static const char * value;
};
template<typename BarType>
const char * Foo<BarType>::value = { BarType::value };

using Bar = Derived<String>;
using FooBar = Foo<Bar>;

int main()
{
    std::cout << "FooBar::value = " << FooBar::value << std::endl;
}

这会打印:

// nothing (Segmentation fault)
  1. 为什么会这样?

  2. 我该如何修复/解决它?

这可以在 Clang 和 Visual-C++ 中重现,但是 GCC 在第二个示例中也会打印 FooBar::value = STRING

更新:Working solution

正如@serge-ballesta 所建议的。我更喜欢这个解决方案,因为它与原始代码非常相似。将 constexpr 成员添加到 VS 时,可以轻松应用并再次轻松删除。

我认为问题来自 [basic.start.init]:

Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization

Derived<Base>::valueFoo<BarType>::value的初始化不是静态初始化——因为右边不是常量表达式。这使得它动态初始化。由于变量是模板特化,因此初始化是 无序 - 也就是说,两个 value 没有明确定义的顺序。

因此,我们有两种可能的顺序。有效的:

Derived<Base>::value ==> 0
Foo<BarType>::value ==> 0
Derived<Base>::value ==> Base::value
Foo<BarType>::value ==> BarType::value

无效的:

Derived<Base>::value ==> 0
Foo<BarType>::value ==> 0
Foo<BarType>::value ==> BarType::value
Derived<Base>::value ==> Base::value

如果Derived<Base>::value先初始化,那么Foo<BarType>::value会指向"STRING"。否则如果先初始化后者,则初始化为0。您看到的分段错误是由于尝试流式传输空字符指针造成的。

@Barry 给出了问题的原因。

一个可能的解决方法是强制执行初始化顺序。由于 String 不是模板化的 class,String::value 将在动态初始化发生之前正确初始化(静态)。

我可以想象两种方式:

  1. Foo添加显式初始化方法,而不是依赖于自动动态初始化:

    ...
    template<typename BarType>
    struct Foo
    {
        static const char * value;
            static void init() {
                Foo::value = BarType::value;
            }
    };
    
    template<typename BarType>
    const char * Foo<BarType>::value;
    
    using Bar = Derived<String>;
    using FooBar = Foo<Bar>;
    
    int main()
    {
        FooBar::init();
        std::cout << "FooBar::value = " << FooBar::value << std::endl;
    }
    
  2. 使 value 成为 Foo 中的函数:

    ...
    template<typename BarType>
    struct Foo
    {
        static const char * value() {
            return BarType::value;;
        }
    };
    
    using Bar = Derived<String>;
    using FooBar = Foo<Bar>;
    
    int main()
    {
        std::cout << "FooBar::value = " << FooBar::value() << std::endl;
    }