4

以下代码在使用g++clang++编译时会产生截然不同的结果。对不起,这个例子很长,但我无法让它更短。

程序应将特定位位置分配给特定类型,然后构建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;
}
  • g++ 4.8.2打印-----: 00000000000000000000000000000001
  • clang++ SVN打印:( 00000000000000000000000000001111 如预期的那样)

只有编译标志是-std=c++11.

怎么了?我是否引入了未定义的行为?g++错了吗?

4

3 回答 3

6

您的代码依赖于两个声明的初始化顺序:

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

如果所有IdStore<T>::bitIdx初始化都发生在之前,BtsStore<A...>::bts那么您将获得预期的行为。如果所有这些都发生在你之后BtsStore<A...>::bts,你就会得到 g++ 行为。标准允许这两种排序:

3.6.2 非局部变量的初始化

2 (...) 具有静态存储持续时间的非局部变量的动态初始化是有序的或无序的。显式专门化的类模板静态数据成员的定义已排序初始化。其他类模板静态数据成员(即隐式或显式实例化的特化)具有无序初始化

于 2013-10-24T11:56:14.000 回答
2

解决方案是使IdStore::bitIdx静态成员函数返回静态局部变量。

于 2013-10-24T12:34:46.947 回答
1

添加到zch的答案

在定义这些类型之后强制 for 、 和的实例化IdStore似乎Type1可以Type2解决问题。为此,在、和的定义之后添加这些行。Type3Type4Type1Type2Type3Type4

template struct IdStore<Type1>;
template struct IdStore<Type2>;
template struct IdStore<Type3>;
template struct IdStore<Type4>;

更新:像 Vittorio 一样,我不再喜欢上面的解决方案。这是另一个。变成IdStore模板函数:

template <typename T>
int IdStore() {
    return getNextId();
}

并且,以buildBtsHelper这种方式使用它:

mBts[IdStore<T>()] = true;
于 2013-10-24T12:09:33.037 回答