2013-10-24 2 views
4

Следующий код дает совершенно разные результаты при компиляции с г ++ или лязгом ++. Извините за длинный пример, но я не смог сделать его короче.г ++ и лязг различное поведение с рекурсивной VARIADIC создания шаблона BITSET (возможно GCC ошибка?)

Программа должна назначить конкретную позицию бит конкретному типу, а затем построить 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; 
} 
  • г ++ 4.8.2 принты -----: 00000000000000000000000000000001
  • лязгом ++ SVN печатает: 00000000000000000000000000001111(как и ожидалось)

только флаг компиляции -std=c++11 ,

Что происходит? Я представляю неопределенное поведение? Является ли g ++ неправильным?

+0

Относительно вашего заявления о том, что «Только флаг компиляции -std = C++ 11»: хотя в этом случае не может быть ни одного, когда у вас есть проблема, всегда включайте столько предупреждений, сколько сможете. –

+0

Хотя я не знаю, является ли это ошибкой, эта программа не «назначает конкретную позицию бита определенному типу во время компиляции», если только из-за правила as-if. Он работает во время инициализации. Вы не можете использовать функции non-constexpr во время компиляции. – zch

+0

@zch: Да, извините, я использовал неправильный термин. Фиксация OP. –

ответ

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 (...) Динамическая инициализация нелокального переменный со статической памятью длительности либо заказать или неупорядоченных. Определения явно шаблонов статических данных шаблона специализированного класса заказали . Другие статические элементы шаблона шаблона класса (т. Е. неявно или явно созданные экземпляры) имеют неупорядоченные инициализацию.

+0

Спасибо. Как я могу принудительно инициализировать 'IdStore ' перед всем остальным? –

+0

@VittorioRomeo: Я разместил заявку на новый вопрос. –

+0

@VittorioRomeo, я бы сделал это во время компиляции, но это может быть немного продвинутым. См. Http://stackoverflow.com/questions/16707645/how-to-get-element-from-stdtuple-by-type – zch

1

Добавить в zch's answer.

Принуждение instatiations из IdStore для Type1, Type2, Type3 и Type4 только после того, как эти типы определены, кажется исправить эту проблему. Для этого добавьте эти строки сразу после определений Type1, Type2, Type3 и Type4.

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

Update: Как Витторио, я больше не как выше решения. Вот еще один.Включите IdStore в функцию шаблона:

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

и, в buildBtsHelper использовать его таким образом:

mBts[IdStore<T>()] = true; 
+0

Мне не нравится это решение, так как система предназначена для использования в системе сущностей на основе компонентов, где существует около 30-40 типов ... и кажется, что это раздражает, когда пользователь должен определить все эти типы –

+0

@VittorioRomeo: Я тоже ;-) Итак, я придумал то, что мне лучше (см. обновление). Он использует старый трюк, который заменяет статическую переменную функцией, которая возвращает локальную статическую переменную (здесь это делается косвенно через 'getNextId'). Таким образом, мы уверены, что значение инициализируется в первый раз, когда оно используется. –

+1

Сделать его 'static id = getNextId(); return id; 'и он мог бы сделать как обходной путь, я думаю. – zch

2

Решение сделать IdStore::bitIdx статическую функцию-член возвращает статическую локальную переменную.