4

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

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

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还有一个在本地存储、静态存储和模板变量中实例化一些s 的程序,这是该程序:

// 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;
}

这是程序,这是输出:

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上阅读了以下句子(强调我的):

允许实现在与允许实现在命名空间范围(3.6.2)中静态初始化具有静态或线程存储持续时间的变量相同的条件下,对具有静态或线程存储持续时间的其他块范围变量执行早期初始化。否则,此类变量在控件第一次通过其声明时被初始化;这样的变量在其初始化完成时被认为已初始化。如果初始化抛出异常退出,说明初始化未完成,下次控件进入声明时会再次尝试。如果控制在变量初始化的同时进入声明,并发执行将等待初始化完成.

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

谢谢。

4

2 回答 2

1

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

隐式实例化的类、函数和变量模板特化放置在定义模板的命名空间中。

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

于 2015-03-24T12:38:08.197 回答
1

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

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

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

具有静态存储持续时间 (3.7.1) 或线程存储持续时间 (3.7.2) 的变量应在任何其他初始化发生之前进行零初始化 (8.5)。

...

如果变量是隐式或显式实例化的特化,则具有静态存储持续时间的非局部变量的动态初始化是无序的,否则是有序的[注意:显式特化的静态数据成员或变量模板特化已排序初始化。—<em>尾注] 在单个翻译单元中定义的具有有序初始化的变量应按照它们在翻译单元中的定义顺序进行初始化。如果程序启动线程(30.3),则变量的后续初始化相对于在不同翻译单元中定义的变量的初始化是无序的。否则,变量的初始化相对于在不同翻译单元中定义的变量的初始化是不确定的。如果程序启动一个线程,则变量的后续无序初始化相对于其他所有动态初始化都是无序的。否则,

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

于 2015-03-24T15:20:16.843 回答