g++ and clang different behavior with recursive variadic template bitset creation (possible gcc bug?)

StackOverflow https://stackoverflow.com/questions/19563891

Pergunta

The following code produces drastically different results when compiled with g++ or with 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.

#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 prints-----: 00000000000000000000000000000001
  • clang++ SVN prints: 00000000000000000000000000001111 (as expected)

Only compilation flag is -std=c++11.

What is happening? Am I introducing undefined behavior? Is g++ wrong?

Foi útil?

Solução

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. If all of the happen after BtsStore<A...>::bts then you get g++ behavior. Both orderings are allowed by standard:

3.6.2 Initialization of non-local variables

2 (...) Dynamic initialization of a non-local variable with static storage duration is either ordered or unordered. Definitions of explicitly specialized class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) have unordered initialization.

Outras dicas

The solution is to make IdStore::bitIdx a static member function returning a static local variable.

Adding to zch's answer.

Forcing the instatiations of IdStore for Type1, Type2, Type3 and Type4 just after these types are defined, seems to fix the problem. For this, add these lines just after the definitions of Type1, Type2, Type3 and 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:

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

and, in buildBtsHelper use it this way:

mBts[IdStore<T>()] = true;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top