2015-10-05 121 views
4

我有一些C++ 11模板代码我试图移植到Visual C++编译器2015.原始代码工作得很好,但是我需要重写它以解决问题与constexpr。为什么静态const char *模板struct成员未初始化

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

至于建议由@哔叽,贝勒斯特。我喜欢这个解决方案,因为它与原始代码非常相似。当constexpr成员添加到VS时易于应用和轻松删除。

+3

为什么_what_发生?是[输出'STRING'](http://coliru.stacked-crooked.com/a/21f5265df51c0047)不是你所期望的? –

+0

有人编辑了非工作代码的链接。生病了。 STRING预计。 – Mathias

+1

用'template class Derived 明确实例化这两个类; template struct Foo >;'使用前[解决段错误](http://melpon.org/wandbox/permlink/fkZHNjXNrMA1gAO8) –

回答

2

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

非本地变量,静态存储持续时间的动态初始化无序如果变量是或明或暗地实例化专业化

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。您看到的分段错误是由于尝试传输空字符指针造成的。

1

@巴里给出了这个问题的原因。

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

我能想象2种方式:

  1. 添加一个明确的init方法来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; 
    } 
    
+0

谢谢!将@Barry的回答标为正确,因为'为什么'是问题中最有趣的部分。您的第二个解决方案与原始代码非常类似。我认为这是最好的途径,当constexpr在VS中成熟时,然后恢复更改将变得很容易。我正在为该问题添加一个解决方案示例。 – Mathias