【发布时间】:2015-09-21 21:43:16
【问题描述】:
我编写了一个基于两种类型的类模板,根据其模板参数为其分配了唯一索引:
template<typename SK,typename T>
struct Component {
static uint const index;
};
期望对于每个新类型,index 都会递增:
Component<X,A>::index; // 0
Component<X,B>::index; // 1
Component<Y,A>::index; // 0
Component<Y,B>::index; // 1
// ...etc
分配索引的完整代码如下:
using uint = unsigned int;
template<typename SK,typename T>
struct Component
{
static uint const index;
};
template<typename SK>
class ComponentCount
{
template<typename CSK,typename CT>
friend struct Component;
private:
template<typename T>
static uint next() {
return ComponentCount<SK>::get_counter();
}
static uint get_counter()
{
static uint counter = 0;
return counter++;
}
};
这在 GCC (5.1) 和 MSVC 中按预期工作,并进行了以下测试:
// global scope
struct X {};
struct Y {};
int main()
{
// function scope
struct Z{};
uint x0 = Component<X,int>::index;
uint x1 = Component<X,double>::index;
uint x2 = Component<X,double>::index;
uint x3 = Component<X,std::string>::index;
uint x4 = Component<X,int>::index;
uint x5 = Component<X,int>::index;
std::cout << x0 << ", " << x1 << ", " << x2 << ", "
<< x3 << ", " << x4 << ", " << x5 << std::endl;
uint y0 = Component<Y,int>::index;
uint y1 = Component<Y,double>::index;
uint y2 = Component<Y,double>::index;
uint y3 = Component<Y,std::string>::index;
uint y4 = Component<Y,int>::index;
uint y5 = Component<Y,int>::index;
std::cout << y0 << ", " << y1 << ", " << y2 << ", "
<< y3 << ", " << y4 << ", " << y5 << std::endl;
uint z0 = Component<Z,int>::index;
uint z1 = Component<Z,double>::index;
uint z2 = Component<Z,double>::index;
uint z3 = Component<Z,std::string>::index;
uint z4 = Component<Z,int>::index;
uint z5 = Component<Z,int>::index;
std::cout << z0 << ", " << z1 << ", " << z2 << ", "
<< z3 << ", " << z4 << ", " << z5 << std::endl;
return 0;
}
输出是
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
但是对于 Clang (3.6.1),输出不同:
0, 1, 1, 2, 0, 0
0, 1, 1, 2, 0, 0
5, 2, 2, 3, 5, 5
具体来说,为函数局部类型(即'Z')生成的索引会做一些奇怪的事情。就像每次调用 Component<Z,...> 时它们都会增加并重新分配索引。
为什么会这样?它是编译器错误吗?将函数本地类型与模板一起使用时(C++11 后)是否有任何特殊注意事项?
一个完整的例子可以在这里找到: http://coliru.stacked-crooked.com/a/7fcb989ae6eab476
== 编辑 ==
我决定将问题发布到 clang 的 bugtracker,所以如果其他人遇到这个问题:
【问题讨论】:
-
看起来 clang 期望
Component<SK, T>::index的初始化表达式是纯的。删除index上的const说明符会得到expected result。 -
我不完全确定这个程序是否格式正确;也许它格式正确,但输出未指定。例如,静态数据成员的不同实例化应该具有无序初始化。然而,初始化的顺序决定了程序的输出。
-
函数内的实例化顺序是否定义明确也不清楚。但是,我看不出有任何理由让
5或3出现在输出中(我相信这需要UB,而且我在这里看不到任何UB,只有未指定的顺序)。另见groups.google.com/a/isocpp.org/d/msg/std-discussion/M6aJMH_ewoM/…