我正在使用C ++ 14中的新模板变量功能以便习惯它(可能很快就会这样做'因为似乎有些编译器没有完全实现它。)
现在我想知道每个模板变量实例的位置。在我到目前为止所做的测试中,它们似乎在任何静态数据之前被初始化,因此我想知道它们是否被放置在程序的data segment中。让我们看看到目前为止我尝试了什么,我有一个类打印有关构造和破坏的信息:
struct squealer
{
squealer(std::string a_name) : m_name(a_name) { std::cout << this << ' ' << m_name << ' ' << __PRETTY_FUNCTION__ << '\n'; }
~squealer() { std::cout << this << ' ' << m_name << ' ' << __PRETTY_FUNCTION__ << '\n'; }
void f() {}
const std::string m_name;
};
在本地存储,静态存储和模板变量中实例化一些squealer
的程序,这是程序:
// static storage squealer
squealer s("\"static\"");
// template variable squealer
template <int i> squealer test(std::string(i, 'A'));
// function using a template variable squealer
void f() { test<1>.f(); }
int main(int argc, char **argv)
{
// local storage squealer
squealer ss("local");
// using another template variable squealers
test<2>.f();
switch (argc)
{
case 1: test<3>.f(); break;
case 2: test<4>.f(); break;
case 3: test<5>.f(); break;
case 4: test<6>.f(); break;
}
return 0;
}
Here is the program这是输出:
A squealer::squealer(std::string)
AA squealer::squealer(std::string)
AAA squealer::squealer(std::string)
AAAA squealer::squealer(std::string)
AAAAA squealer::squealer(std::string)
AAAAAA squealer::squealer(std::string)
"static" squealer::squealer(std::string)
local squealer::squealer(std::string)
local squealer::~squealer()
"static" squealer::~squealer()
AAAAAA squealer::~squealer()
AAAAA squealer::~squealer()
AAAA squealer::~squealer()
AAA squealer::~squealer()
AA squealer::~squealer()
A squealer::~squealer()
正如我们所看到的,所有模板变量squealer
实例都是在名为"static"
的实例之前创建的,并且在结束时(如预期的那样)创建名为local
的实例,销毁顺序是相反的(正如预期的那样),因此:模板变量实例的创建/初始化顺序与其在代码上的外观相同,无论该外观的位置如何,无论它们是否被使用({永远不会调用{1}}函数。
所以第一个问题是,这个模板变量是否放在数据段上?我不知道如何测试或检查它。
第二个问题是,所有这些模板变量f()
实例都是线程安全的吗?我已经开始n3376 §6.7以下一句话(强调我的):
允许实现在静态或线程存储持续时间内执行其他块范围变量的早期初始化,条件是允许实现在命名空间范围内静态初始化具有静态或线程存储持续时间的变量(3.6.2) )。 否则在第一次控件通过其声明时初始化这样的变量;这样的变量在初始化完成后被认为是初始化的。如果通过抛出异常退出初始化,则初始化未完成,因此下次控制进入声明时将再次尝试初始化。 如果控件在初始化变量时同时进入声明,则并发执行应等待初始化完成。
从C ++ 11开始,如果所有模板变量squealer
实例都在静态存储中,那么它们应该是线程安全的,不是吗?
感谢。
答案 0 :(得分:1)
变量模板特化是静态变量,如[temp.inst] / 12所述:
隐式实例化的类,函数和变量模板特化放置在定义模板的命名空间中。
因此,通常的静态初始化规则适用,这意味着在main()
执行之前初始化所有特化。
答案 1 :(得分:1)
您引用的标准部分描述了具有静态存储持续时间的块范围变量,例如:
int foo() {
static int bar = 42;
return bar;
}
你的程序没有。具有静态存储持续时间的所有变量都在命名空间范围内声明,因此您需要查看[basic.start.init](3.6.2)。特别是第二段陈述:
具有静态存储持续时间(3.7.1)或线程存储持续时间(3.7.2)的变量在进行任何其他初始化之前应进行零初始化(8.5)。
...
如果变量是隐式或显式实例化的特化,则有序 [注意:明确专业化 静态数据成员或变量模板特化已经有序初始化。 - 结束注释]在单个翻译单元中定义的有序初始化的变量应按其顺序初始化 翻译单位中的定义。如果程序启动一个线程(30.3),则对于在不同转换单元中定义的变量的初始化,后续的变量初始化是未排序的。否则,对于在不同转换单元中定义的变量的初始化,变量的初始化是不确定地排序的。如果程序启动一个线程,则对于每个其他动态初始化,后续无序的变量初始化都是无序的。除此以外, 对于每个其他动态初始化,变量的无序初始化是不确定的。
在问题的程序中,必须动态初始化具有静态存储持续时间的所有squealer
实例,因为squealer
具有无法进行常量初始化的成员std::string
。 ::s
已经命令初始化,并且test
的所有实例都具有无序初始化,因为每个实例都是模板test
的“隐式或显式实例化的特化”。 test
实例保证在输入main
之前被初始化,但是否则所有投注都是关闭的:它们可以按任何顺序初始化,可能在::s
初始化之前和/或之后等等重要的是std::cout
。那些初始化特别是不线程安全:“如果一个程序启动一个线程,那么对于每个其他动态初始化,后续无序的变量初始化都是无序的。”