[英]g++ and clang different behavior with recursive variadic template bitset creation (possible gcc bug?)
The following code produces drastically different results when compiled with g++ or with clang++ . 使用g ++或clang ++编译时,以下代码会产生截然不同的结果。 Sorry for the long example, but I haven't been able to make it any shorter.
很抱歉这个很长的例子,但我还没能做到更短。
The program should assign a specific bit position to a specific type, then build an std::bitset
containing multiple type bits. 程序应将特定位位置分配给特定类型,然后构建包含多个类型位的
std::bitset
。
#include <bitset>
#include <iostream>
using namespace std;
using Bts = bitset<32>;
int getNextId() { static int last{0}; return last++; }
template<class T> struct IdStore{ static const int bitIdx; };
template<class T> const int IdStore<T>::bitIdx{getNextId()};
template<class T> void buildBtsHelper(Bts& mBts) {
mBts[IdStore<T>::bitIdx] = true;
}
template<class T1, class T2, class... A>
void buildBtsHelper(Bts& mBts) {
buildBtsHelper<T1>(mBts); buildBtsHelper<T2, A...>(mBts);
}
template<class... A> Bts getBuildBts() {
Bts result; buildBtsHelper<A...>(result); return result;
}
template<class... A> struct BtsStore{ static const Bts bts; };
template<class... A> const Bts BtsStore<A...>::bts{getBuildBts<A...>()};
template<> const Bts BtsStore<>::bts{};
template<class... A> const Bts& getBtsStore() {
return BtsStore<A...>::bts;
}
struct Type1 { int k; };
struct Type2 { float f; };
struct Type3 { double z; };
struct Type4 { };
int main()
{
cout << getBtsStore<Type1, Type2, Type3, Type4>() << endl;
return 0;
}
00000000000000000000000000000001
00000000000000000000000000000001
00000000000000000000000000001111
(as expected) 00000000000000000000000000001111
(正如预期的那样) Only compilation flag is -std=c++11
. 只有编译标志是
-std=c++11
。
What is happening? 怎么了? Am I introducing undefined behavior?
我是否引入了未定义的行为? Is g++ wrong?
g ++错了吗?
Your code relies on initialization order of two declarations: 您的代码依赖于两个声明的初始化顺序:
template<class T> const int IdStore<T>::bitIdx{getNextId()};
template<class... A> const Bts BtsStore<A...>::bts{getBuildBts<A...>()};
// getBuildBts uses IdStore<T>::bitIdx as indexes to assign
If all of IdStore<T>::bitIdx
initializations happen before BtsStore<A...>::bts
then you get your expected behavior. 如果所有
IdStore<T>::bitIdx
初始化都发生在BtsStore<A...>::bts
那么您将获得预期的行为。 If all of the happen after BtsStore<A...>::bts
then you get g++ behavior. 如果所有这些都发生在
BtsStore<A...>::bts
之后,那么你会得到g ++行为。 Both orderings are allowed by standard: 标准允许两种排序:
3.6.2 Initialization of non-local variables
3.6.2非局部变量的初始化
2 (...) Dynamic initialization of a non-local variable with static storage duration is either ordered or unordered.
2(...)具有静态存储持续时间的非局部变量的动态初始化是有序的或无序的。 Definitions of explicitly specialized class template static data members have ordered initialization.
显式专用类模板的定义静态数据成员已经有序初始化。 Other class template static data members (ie, implicitly or explicitly instantiated specializations) have unordered initialization .
其他类模板静态数据成员(即,隐式或显式实例化的特化)具有无序初始化 。
解决方案是使IdStore::bitIdx
成为静态成员函数,返回静态局部变量。
Adding to zch's answer . 添加到zch的答案 。
Forcing the instatiations of IdStore
for Type1
, Type2
, Type3
and Type4
just after these types are defined, seems to fix the problem. 在定义了这些类型之后,强制对
Type1
, Type2
, Type3
和Type4
强制执行IdStore
的实例, 似乎解决了这个问题。 For this, add these lines just after the definitions of Type1
, Type2
, Type3
and Type4
. 为此,在
Type1
, Type2
, Type3
和Type4
的定义之后添加这些行。
template struct IdStore<Type1>;
template struct IdStore<Type2>;
template struct IdStore<Type3>;
template struct IdStore<Type4>;
Update : Like Vittorio, I no longer like the solution above. 更新 :像维托里奥一样,我不再喜欢上面的解决方案了。 Here is another one.
这是另一个。 Turn
IdStore
into a template function: 将
IdStore
转换为模板函数:
template <typename T>
int IdStore() {
return getNextId();
}
and, in buildBtsHelper
use it this way: 并且,在
buildBtsHelper
使用它:
mBts[IdStore<T>()] = true;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.