我有一些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
但是,当我重写时,一些静态变量未初始化。即使它编译得很好。
#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)
为什么会这样?
如何修复/解决它?
这可以在Clang和Visual-C ++中重现,但GCC也会在第二个例子中打印FooBar::value = STRING
。
正如@ serge-ballesta所建议的那样。我喜欢这个解决方案,因为它与原始代码非常相似。当constexpr成员添加到VS时,轻松应用并轻松删除。
答案 0 :(得分:2)
我认为问题来自[basic.start.init]:
如果变量是隐式或显式实例化的专门化,则静态存储持续时间的非局部变量的动态初始化无序
Derived<Base>::value
和Foo<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 :(得分:1)
@Barry解释了问题的原因。
可能的解决方法是强制初始化顺序。由于String
不是模板化类,因此在动态初始化发生之前,String::value
将被正确初始化(静态)。
我可以想象两种方式:
向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;
}
在Foo中使value
成为一个函数:
...
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;
}