точка инициализации статического элемента шаблона - PullRequest
0 голосов
/ 01 мая 2018

Когда шаблон класса имеет статический член, нам нужно дополнительное (шаблонное) определение этого члена. Теперь это определение на самом деле не создается сразу, скорее, необходимо создать экземпляр окружающего шаблона, а статическое поле нужно использовать «odr-used». Пока все хорошо.

Тем не менее, я получаю удивительное поведение с GCC / Linux. (г ++ 4,7 и 7,2 )

#include <iostream>

using std::cout;
using std::endl;

template<typename T>
class Factory
  {
  public:
    T val;

    Factory()
      : val{}
      {
        cout << "Factory-ctor  val="<<val<<endl;
      }
  };


template<typename T>
class Front
  {
  public:
    static Factory<T> fac;

    Front()
      {
        cout << "Front-ctor    val="<<fac.val<<endl;
        fac.val += 100;
      }

    T&
    operate ()
      {
        cout << "Front-operate val="<<fac.val<<endl;
        ++ fac.val;
        return fac.val;
      }
  };

template<typename T>
Factory<T> Front<T>::fac;


namespace {
  Front<int> front;
  int global_int = front.operate();
}



int
main (int, char**)
  {
    Front<int> fint;

    int& i = fint.operate();
    cout << "main:         val="<<i<<endl;
    cout << "global_int.......="<<global_int<<endl;

    return 0;
  }

В анонимном пространстве имен мы сначала создаем статический экземпляр Front, а затем вызываем на нем функцию operate(), которая использует статический элемент фабрики. И выходные данные, и значения ясно указывают на то, что ctor статического элемента вызывается после того, как было использовано. Что может быть причиной такого поведения? Для меня это кажется нелогичным: если фабрика управляет каким-то ресурсом, ресурс будет пропущен.

~$ g++ --version
g++ (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0

~$ g++ --std=gnu++17 demo.cpp -o demo
~$ ./demo
Front-ctor    val=0
Front-operate val=100
Factory-ctor  val=0
Front-ctor    val=0
Front-operate val=100
main:         val=101
global_int.......=101

Я пробовал также Clang (3.5), который просто segfaults.


PS : очевидный обходной путь - превратить фабрику в синглтон Meyers. Тем не менее, я ожидаю, что система ctors и dtors будет герметичной в такой базовой ситуации (обратите внимание, что мы не имеем в виду какую-либо статичность из других модулей перевода). Таким образом, я в первую очередь заинтересован в рассуждениях , объясняющих это наблюдение.

1 Ответ

0 голосов
/ 01 мая 2018

Обычно это, скорее всего, называют фиаско статического порядка инициализации.

По сути, у нас есть три объекта со статической продолжительностью хранения: front, global_int и Front<int>::fac. Мы имеем от basic.start.dynamic :

Динамическая инициализация нелокальной переменной со статической продолжительностью хранения неупорядочена, если переменная является неявно или явно созданной специализацией, частично упорядочена, если переменная является встроенной переменной, которая не является неявно или явно созданной специализацией, и в противном случае заказывается.

Итак, Front<int>::fac неупорядочен, остальные два упорядочены. Мы знаем, что front инициализируется до global_int, потому что они упорядочены в порядке определения. Но Front<int>::fac неопределенно чередуется с двумя другими.

По сути, сначала происходит статическая инициализация (которая инициализируется нулями), а затем, спустя некоторое время, ваш Factory конструктор фактически запускается - через некоторое время после того, как вы действительно этого хотите.

Одна вещь, которую вы могли бы сделать, это заставить Factory сделать постоянной инициализацию , что опережает динамическую инициализацию. Вы можете сделать это, просто отметив свой конструктор constexpr.

В качестве альтернативы, вы можете заключить ваш static в функцию согласно синглтону Мейерса.

...