c 11 – 模板变量是否安全?他们被置于数据段?

我正在使用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;
};

并且一个程序在本地存储,静态存储和模板变量中实例化一些,这是程序:

// 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的模块,销毁顺序是相反的(如预期的那样),所以:模板变量实例的创建/初始化顺序与其在代码上的外观相同,无论该外观的位置如何,无论它们是否被使用(永远不会调用f()函数).

所以第一个问题是,这个模板变量是否放在数据段上?我不知道如何测试或检查它.

第二个问题是,所有这些模板变量都是squealer实例的线程安全吗?我在n3376 §6.7读了下面的句子(强调我的):

An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope (3.6.2). Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

从C 11开始,如果所有模板变量squealer实例都在静态存储中,它们应该是线程安全的,不是吗?

谢谢.

最佳答案 您引用的标准部分描述了具有静态存储持续时间的块范围变量,例如:

int foo() {
  static int bar = 42;
  return bar;
}

你的程序没有.具有静态存储持续时间的所有变量都在命名空间范围内声明,因此您需要查看[basic.start.init](3.6.2).特别是第二段规定:

Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place.

Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, and otherwise is ordered [ Note: an explicitly specialized
static data member or variable template specialization has ordered initialization. —end note ] Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their
definitions in the translation unit. If a program starts a thread (30.3), the subsequent initialization of a variable is unsequenced with respect to the initialization of a variable defined in a different translation unit. Otherwise, the initialization of a variable is indeterminately sequenced with respect to the initialization of a variable defined in a different translation unit. If a program starts a thread, the subsequent unordered initialization of a variable is unsequenced with respect to every other dynamic initialization. Otherwise,
the unordered initialization of a variable is indeterminately sequenced with respect to every other dynamic initialization.

在问题的程序中,所有具有静态存储持续时间的squealer实例必须动态初始化,因为squealer有一个无法进行常量初始化的成员std :: string. :: s已经命令初始化,并且所有测试实例都具有无序初始化,因为每个实例都是模板测试的“隐式或显式实例化的特化”.测试实例保证在进入main之前被初始化,但是否则所有的注意都是关闭的:它们可以按任何顺序初始化,可能在:: s初始化之前和/或之后,更重要的是std :: cout.这些初始化显然不是线程安全的:“如果一个程序启动一个线程,那么对于每个其他动态初始化,后续无序的变量初始化都是无序的.”

点赞