Сохраняют ли typedefs шаблонов статический порядок инициализации? - PullRequest
3 голосов
/ 10 июля 2009

Внутри того же модуля компиляции стандарт C ++ говорит, что порядок статической инициализации хорошо определен - это порядок объявлений статических объектов. Но при использовании компилятора Sun Studio 12 я сталкиваюсь с неинтуитивным поведением. Я определил шаблонный класс helper<T>, который содержит статический член _data типа T и статическую функцию-член, которая использует _data с именем foo. В моем файле .cpp у меня есть это выше main ():

struct A { /* some definition */ };

typedef helper<int> s0;
typedef helper<A> s1;

Обратите внимание, что typedef для helper<int> стоит перед typedef для helper<A>. Таким образом, согласно стандарту, я ожидал бы, что helper<int>::_data будет построен до helper<A>::_data (помните, _data является статическим членом). На GCC это так, на Sun - нет.

Это проблематично, потому что конструктор А использует helper<int>::_data. У меня только одна единица компиляции, без потенциальной возможности создания helper<A>, поэтому я подумал, что порядок должен быть четко определен. Это ошибка компилятора Sun, или typedef технически не является определением / созданием экземпляра? Я имею в виду, разрешено ли стандартом поведение компилятора Sun

У меня есть следующее main ():

int main()
{
    //Swapping the order of these has no effect on Sun
    s0::foo();
    s1::foo();
}

Нет другого использования s0 или s1.

Ответы [ 2 ]

6 голосов
/ 10 июля 2009

Внутри той же единицы компиляции стандарт C ++ говорит, что порядок статической инициализации четко определен - это порядок объявлений статических объектов.

В показанном вами коде у вас нет объявления статического члена данных. У вас есть объявление typedef-name. Это не имеет к этому никакого отношения и не влияет на порядок. Вы, вероятно, думаете об этом :

Если я сделаю это объявление typedef, оно создаст экземпляр helper<int> и, таким образом, сначала создаст объявление своего статического члена-члена.

Проблема в том, что эта строка не вызывает создание экземпляра helper<int>. Чтобы это произошло, вам потребуется явное создание экземпляра или управление для его неявного создания экземпляра (например, создание объекта helper<int> или использование его в качестве вложенного спецификатора имени, как в helper<int>::..., и явная ссылка на статический член в противном случае его создание опущено).

Но есть гораздо более глубокая проблема. Порядок не декларация статических данных-членов. Порядок их определение . Рассмотрим следующее

struct C { C() { printf("hey\n"); } };
struct A { 
  static C a;
  static C b;
};

C A::b;
C A::a;

В этом коде b создается перед , даже если a объявлено до b .

Следующий код печатает 2 1:

struct C { C(int n) { printf("%d\n", n); } };

template<int N>
struct A {
  static C c;
};

template<int N>
C A<N>::c(N);

// explicit instantiation of declaration and definition
template struct A<2>;
template struct A<1>;

int main() {

}

Но следующий код ничего не печатает, если вы не прокомментируете строку в main.

struct C { C(int n) { printf("%d\n", n); } };

template<int N>
struct A {
  static C c;
};

template<int N>
C A<N>::c(N);

// implicit instantiation of declarations
A<2> a2;
A<1> a1;

int main() {
  // A<1>::c; A<2>::c;
}

Я не совсем уверен, что правильный вывод для этого второго фрагмента. Читая стандарт, я не могу определить порядок. В 14.6.4.1 «Точка инстанции» говорится:

Для специализации шаблона функции, специализации шаблона функции-члена или специализации для функции-члена или статического члена данных шаблона класса, если специализация создается неявно, так как на нее ссылаются из другой специализации шаблона [... ]. В противном случае точка создания такой специализации сразу следует за объявлением или определением области пространства имен, относящимся к специализации.

Точка создания их определений появляется сразу после определения main. Какое определение создается до того, как другое определение, кажется, не определено. Если кто-то знает ответ и как другие компиляторы ведут себя (GCC печатает 1 2, но с порядком выражений в main swapped, печатает 2 1), пожалуйста, дайте мне знать в комментарии.

Подробнее см. этот ответ о времени жизни статического объекта .

0 голосов
/ 10 июля 2009

Вы на самом деле не объявляете никаких объектов в этом коде.

Вам нужен дополнительный код:

s0 one;
s1 two;

В этом случае два объекта теперь фактически объявлены и должны работать правильно.

Вы явно объявляете s0?

Попробуйте следовать за typedef с манекеном s0; и посмотрите, решена ли проблема.

...