为什么 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
但是,当我重写时,一些静态变量没有被初始化。即使它编译得很好。
#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 时,可以轻松应用并再次轻松删除。
我认为问题来自 [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>::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
。您看到的分段错误是由于尝试流式传输空字符指针造成的。
@Barry 给出了问题的原因。
一个可能的解决方法是强制执行初始化顺序。由于 String
不是模板化的 class,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;
}
使 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;
}
我有一些 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 时,可以轻松应用并再次轻松删除。
我认为问题来自 [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>::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
。您看到的分段错误是由于尝试流式传输空字符指针造成的。
@Barry 给出了问题的原因。
一个可能的解决方法是强制执行初始化顺序。由于 String
不是模板化的 class,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; }
使
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; }