在main之前在templated类的静态容器中插入内容

时间:2013-07-03 08:28:17

标签: c++ templates

我想构建一个仅包含静态数据成员和函数的模板化类,基本上是一些函数集合,其中包含一些内部数据,我想填写代码的各个部分。我想在到达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;  

我在这里缺少什么?为什么我可以通过mainbar之前向非模板类添加内容,但我不能通过wobble执行相同操作?有没有办法像FooBar一样使用模板化的类来获得相同的行为?

3 个答案:

答案 0 :(得分:3)

[basic.start.init] / 2

  

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

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

[intro.execution] / 13

  

A时,评估A和B是不确定的   在A或B之前对B或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的一半)。

答案 1 :(得分:1)

您可以在使用前在Wobble template class Quux<unsigned>;中添加显式模板实例化 像这样的东西 http://ideone.com/clone/mkJo0W

答案 2 :(得分:1)

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

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

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