模板变量线程安全吗?他们被放在数据段?

Are template variables thread safe? they're placed on data segment?

我正在使用 C++14 中的新模板变量功能来适应它(也许很快就会这样做,因为似乎有些编译器没有完全实现它)。

现在我想知道模板变量的每个实例在哪里。在我到目前为止所做的测试中,它们似乎在任何静态数据之前被初始化,所以我想知道它们是否被放置在程序的 data segment 中。让我们看看到目前为止我尝试了什么,我有一个 class 打印关于构造和破坏的信息:

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 的实例,销毁顺序是相反的(正如预期的那样),因此:模板变量实例的 creation/initialization 的顺序与其在代码中的外观相同,而不管这种外观的位置如何,也不管它们被使用或不是(永远不会调用 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 实例都在静态存储中,它们应该是线程安全的,不是吗?

谢谢。

变量模板特化是静态变量,如 [temp.inst]/12 所述:

Implicitly instantiated class, function, and variable template specializations are placed in the namespace where the template is defined.

因此,通常的静态初始化规则适用,这意味着所有特化都在 main() 执行之前初始化。

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

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

其中您的程序有 none。所有具有静态存储持续时间的变量都在命名空间范围内声明,因此您需要查看 [[=​​35=]] (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有一个成员std::string不能常量初始化,所有具有静态存储持续时间的squealer实例必须动态初始化。 ::s 进行了有序初始化,而 test 的所有实例都进行了无序初始化,因为每个实例都是模板 test 的 "implicitly or explicitly instantiated specialization"。 test 实例保证在进入 main 之前被初始化,但除此之外,所有的赌注都不成立:它们可以以任何顺序初始化,可能在 and/or 之前 ::s 初始化之后更重要的是 std::cout。这些初始化特别是不是线程安全的:"If a program starts a thread, the subsequent unordered initialization of a variable is unsequenced with respect to every other dynamic initialization."