使用Clang中具有函数本地类型的模板的类型计数器出现意外结果
Unexpected result for a type counter using templates with function local types in Clang
我编写了一个基于两种类型的类模板,并根据其模板参数为其分配了唯一索引:
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的错误跟踪器上,所以如果其他人遇到这个问题:
https://llvm.org/bugs/show_bug.cgi?id=24048
这对我来说像是一个bug。我不知道C++11规则应该会对函数的局部类型和全局类型产生影响。
如果转储汇编程序,您可以注意到,对于X
和Y
,值是实际计算的,而对于Z
,它们是预先计算的。计数器静态变量初始化的保护变量根本没有生成。
.Ltmp87:
#DEBUG_VALUE: main:z5 <- 5
#DEBUG_VALUE: main:z4 <- 5
#DEBUG_VALUE: main:z3 <- 3
#DEBUG_VALUE: main:z2 <- 2
#DEBUG_VALUE: main:z1 <- 2
#DEBUG_VALUE: main:z0 <- 5
.loc 6 54 5 # main.cpp:54:5
movl std::cout, %edi
movl $5, %esi
.loc 6 74 5 # main.cpp:74:5
callq std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int)
我不知道这是否是clang中的一个错误,但接下来您的当前版本Component::template似乎有问题。
为了说明这一点,我将功能成员的访问权限从"private"更改为"public"。然后使用以下代码:
for(int i=0; i<5;++i)
std::cout<< ComponentCount<int>::next<int>() <<" ";
我得到:
0 1 2 3 4
所以,也许你应该考虑将next的实现更改为这样的东西:
template<typename SK>
class ComponentCount
{
template<typename CSK,typename CT>
friend struct Component;
private:
template<typename T>
static uint next() {
static uint index = get_counter();
return index;
}
static uint get_counter()
{
static uint counter = 0;
return counter++;
}
};
有了这个,我在gcc、clang3.6和VS2015 上得到了预期的结果
g++(GCC(5.1.0版权所有(C(2015自由软件基金会,股份有限公司本是自由软件;有关复制条件,请参见来源。没有担保甚至不考虑适销性或特定产品的适用性目的。
0,1,1,2,0,0
0,1,1,2,0,0
0,1,1,2,0,0
clang 3.6.0版(标签/RERELEASE_360/最终235480(目标:x86_64-unknown-linux-gnu线程型号:posix
0,1,1,2,0,0
0,1,1,2,0,0
0,1,1,2,0,0
最后的代码+运行gcc&coliru 发出叮当声
[edit]打字错误
- ArduinoJson 6.15.2:JsonObject没有命名类型
- 防止主数据类型C++的隐式转换
- 大量序列中核苷酸类型的快速计数
- 如何从C++中的依赖类型中获得它所依赖的类型
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 是否可以初始化不可复制类型的成员变量(或基类)
- 如何获取std::result_of函数的返回类型
- 从父命名空间重载类型
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- Openssl 1.1.1d无效使用不完整的类型"struct dsa_st"
- 访问者访问变体并返回不同类型时出错
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 不需要增量锁的 C++ 计数器类型
- 我想开发一个函数,它接受任何类型的向量并返回一个映射(类似于 python 中的计数器)
- 使用“size_t”作为计数器的类型
- 为小循环计数器选择什么数字类型
- for循环需要运行LLONG_MAX次数,但是计数器变量也需要表示有符号的数字.你用什么类型的柜台?
- 使用Clang中具有函数本地类型的模板的类型计数器出现意外结果