Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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++. 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?

like image 254
Vittorio Romeo Avatar asked Jan 13 '23 02:01

Vittorio Romeo


1 Answers

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.

like image 148
zch Avatar answered Jan 16 '23 19:01

zch