2

我想构建一个只有静态数据成员和函数的模板类,基本上是一个带有一些内部数据的函数集合,我想填写代码的各个部分。我试图在到达之前将内容插入到数据成员中main()。这适用于非模板类,但对于模板类,我似乎无法弄清楚如何使其工作。

这是代码:

#include <iostream>
#include <vector>

class Foo{
private:
    static std::vector<unsigned> content;
public:
    static void insert(unsigned u){ content.push_back(u); }
    static size_t size(){ return content.size(); }
};
std::vector<unsigned> Foo::content=std::vector<unsigned>();

struct Bar{ 
    Bar(){ Foo::insert(0);  }
} bar; 
// this works fine in gcc, but is this consistent or am I lucky?
// Foo::content will contain 0 prior to entering main

template <typename T>
class Quux{
private:
    static std::vector<T> content;
public:
    static void insert(T t){ content.push_back(t); }
    static size_t size(){ return content.size(); }
};
template <typename T>
std::vector<T> Quux<T>::content=std::vector<T>();

struct Wobble{ 
    Wobble(){ Quux<unsigned>::insert(0);    }
} wobble;  
// this does not work
// Quux<unsigned>::content will be empty prior to entering main

int main(){

    std::cout << Foo::size() << std::endl;  
    // outputs 1, as desired

    std::cout << Quux<unsigned>::size() << std::endl;   
    // outputs 0, makes me sad :(

    Wobble wobble2;
    std::cout << Quux<unsigned>::size() << std::endl;   
    // outputs 1, as desired
}

输出:

1
0
1

Foo是非模板类,我可以在通过结构Foo::content运行之前插入东西就好了。我希望这是一致的行为,而不是我很幸运?main()Bar

但是,当我尝试为模板类做同样的事情Quux<T>时,似乎我必须等到main()我才能添加东西。有人可以解释为什么这是必要的,并且(希望)能解决这个问题吗?我认为它与模板实例化的时间有关,但我无法弄清楚为什么。我希望Quux<unsigned>在以下之后完全可用:

struct Wobble{ 
    Wobble(){ Quux<unsigned>::insert(0); }
} wobble;  

我在这里想念什么?main为什么我可以在via之前向非模板类添加内容,bar但我不能通过 via 做同样的事情wobbleFoo有没有办法获得与Bar模板类相同的行为?

4

3 回答 3

3

[basic.start.init]/2

显式专门化的类模板静态数据成员的定义已排序初始化。其他类模板静态数据成员(即隐式或显式实例化的特化)具有无序初始化。[...] 在单个翻译单元中定义的具有有序初始化的变量应按照它们在翻译单元中的定义顺序进行初始化。[...]否则,变量的无序初始化相对于其他所有动态初始化是不确定的。

据我了解,您有未定义的行为,因为 的初始化Quux<unsigned>::content与初始化的顺序不确定wobble

[intro.execution]/13

当 A 在 B 之前排序或 B 在 A 之前排序时,评估 A 和 B 的排序不确定,但未指定哪个。

也就是说,您的程序可能会访问未动态初始化的Quux<unsigned>::content.

明确的专业化解决了这个问题。


请注意,在任何动态初始化之前,都会发生零初始化。因此,可以使用指针和动态内存分配来克服初始化顺序的问题:

template <typename T>
class Quux{
private:
    static std::vector<T>* content;
    static void create() { if(!content) content = new std::vector<T>; };
public:
    static void insert(T t){ create(); content->push_back(t); }
    static size_t size(){ create(); return content->size(); }
};
template <typename T>
std::vector<T>* Quux<T>::content;

这将在程序结束时引入“内存泄漏”;如果这是一个问题,您可以添加一个删除器对象,即另一个content在自身销毁时删除的静态数据成员(半个 RAII)。

于 2013-07-03T08:50:14.373 回答
1

您可以在 Wobble 中使用之前添加显式模板实例化template class Quux<unsigned>;
类似 http://ideone.com/clone/mkJo0W

于 2013-07-03T08:48:18.117 回答
1

您应该在定义第一个摆动之前为无符号添加内容向量的显式实例化,即

template<>
std::vector<unsigned> Quux<unsigned>::content=std::vector<unsigned>();

然后它按预期工作,输出 1、1、2。

于 2013-07-03T08:49:38.560 回答