我想构建一个仅包含静态数据成员和函数的模板化类,基本上是一些函数集合,其中包含一些内部数据,我想填写代码的各个部分。我想在到达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
在bar
之前向非模板类添加内容,但我不能通过wobble
执行相同操作?有没有办法像Foo
和Bar
一样使用模板化的类来获得相同的行为?
答案 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。