Статическая членская инициализация C++ (обрабатывают забаву по шаблону внутри),

Наименьшее количество кода было бы:

>>> s = set([1, 2, 3])
>>> list(s)[0]
1

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

38
задан Cœur 3 February 2018 в 13:02
поделиться

3 ответа

Это обсуждалось в usenet некоторое время назад, когда я пытался ответить на другой вопрос о stackoverflow: Точка создания статических элементов данных . Я думаю, что стоит сократить тестовый пример и рассматривать каждый сценарий в отдельности, поэтому давайте сначала рассмотрим его в более общем плане:


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); 

A<1> a; // implicit instantiation of A<1> and 2
A<2> b;

У вас есть определение шаблона статического элемента данных. Это еще не создает никаких элементов данных из-за 14.7.1 :

"... в частности, инициализация (и любые связанные побочные эффекты) статического элемента данных не происходит, если только сам статический элемент данных используется таким образом, который требует существования определения статического элемента данных. "

Определение чего-либо (= сущности) необходимо, когда эта сущность« используется », согласно одному правилу определения, которое определяет это слово ( 3.2 / 2 ). В частности, если все ссылки взяты из неустановленных шаблонов, членов шаблона или выражений sizeof или подобных вещей, которые не «используют» сущность (поскольку они либо не оценивают ее, либо просто еще не существуют как функции / функции-члены, которые используются сами), такой статический элемент данных не создается.

Неявное создание экземпляра 14.7.1 / 7 создает экземпляры объявлений статических элементов данных - то есть он создаст экземпляр любого шаблона, необходимого для обработки этого объявления. Однако он не будет создавать экземпляры определений, то есть инициализаторы не создаются, а конструкторы типа этого статического элемента данных не определены неявно (помечены как используемые).

Это означает, что приведенный выше код пока ничего не выводит. Теперь вызовем неявные экземпляры статических элементов данных.

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

Это приведет к существованию двух статических элементов данных, но вопрос в том, каков порядок инициализации? При простом чтении можно подумать, что применяется 3.6.2 / 1 , в котором говорится (выделено мной):

«Объекты со статической продолжительностью хранения, определенной в области пространства имен в том же переводе unit и динамически инициализированные должны быть инициализированы в том порядке, в котором их определения появляются в блоке перевода. "

Теперь, как сказано в сообщении usenet и объяснено в этом отчете о дефектах , эти статические элементы данных не определены в единице перевода, но они создаются в единице создания экземпляра , как описано в 2.1 / 1 :

Каждая переведенная единица трансляции проверяется на создать список необходимых экземпляров. [Примечание: сюда могут входить экземпляры, которые были явно запрошены (14.7.2). ] Определения необходимых шаблонов находятся. Это определяется реализацией, требуется ли доступность источника единиц перевода, содержащих эти определения. [Примечание: реализация может закодировать достаточно информации в переведенной единице перевода, чтобы гарантировать, что источник здесь не требуется. ] Все необходимые экземпляры выполняются для создания экземпляров экземпляров. [Примечание: они похожи на переведенные единицы перевода, но не содержат ссылок на неустановленные шаблоны и определения шаблонов. ] Программа плохо сформирована, если какой-либо экземпляр не удается.

Точка создания такого члена также не имеет большого значения, потому что такая точка создания экземпляра является контекстной связью между экземпляром и его единицами трансляции - она ​​определяет объявления, которые являются видимыми (как указано в 14.6.4.1 , и каждая из этих точек создания экземпляров должна давать экземплярам то же значение, что указано в одном правиле определения в 3.2 / 5 , последняя пуля).

Если мы хотим упорядоченную инициализацию, мы должны организовать так, чтобы мы не возились с экземплярами, а с явными объявлениями - это область явных специализаций, поскольку они на самом деле не отличаются от обычных объявлений. По факту, Определения явно специализированных членов статических данных шаблона класса упорядочили инициализацию. Другой Члены статических данных шаблона класса (т. е. неявно или явно созданные экземпляры специализаций) имеют неупорядоченную инициализацию.


Это означает для вашего кода, что:

  • [1] и [2] прокомментировали: Ссылки на статические элементы данных отсутствуют, поэтому их определения (а также их объявления, поскольку нет необходимости в создании экземпляра B ) не создаются. Побочных эффектов не возникает.
  • [1] раскомментировано: используется B :: getB () , который сам по себе использует B :: mB , что требует статического член существовать. Строка инициализируется до main (в любом случае до этого оператора, как часть инициализации нелокальных объектов). Ничто не использует B :: mInit , поэтому он не создается, и поэтому нет объекта B :: InitHelper создается когда-либо, что делает его конструктор не используемым, который, в свою очередь, никогда не будет назначать что-либо для B :: mB : вы просто выведете пустую строку.
  • [1] и [2] раскомментировали: То, что это сработало для вас, - удача (или наоборот :)). Как объяснено выше, нет требований к определенному порядку вызовов инициализации. Он может работать на VC ++, терпеть неудачу на GCC и работать на clang. Мы не знаем.
  • [1] прокомментировал, [2] раскомментировал: Та же проблема - опять же, оба статических элемента данных используются : B :: mInit используется B :: getHelper , и создание экземпляра B :: mInit вызовет создание экземпляра его конструктора, который будет использовать ] B :: mB - но для вашего компилятора порядок отличается в этом конкретном прогоне (неопределенное поведение не требуется для согласованности между разными прогонами): сначала инициализируется B :: mInit , который будет работать с еще не построенной строкой объект.
37
ответ дан 27 November 2019 в 03:52
поделиться

Проблема в том, что определения, которые вы даете для статических переменных-членов, также являются шаблонами.

template<class T>
std::string B<T>::mB;
template<class T>
typename B<T>::InitHelper B<T>::mInit;

Во время компиляции это фактически ничего не определяет, так как T неизвестен. Это что-то вроде объявления класса или определения шаблона, компилятор не генерирует код и не резервирует память, когда его видит.

Определение происходит неявно позже, когда вы используете класс шаблона. Поскольку в случае segfaulting вы не используете B :: mInit, он никогда не создается.

Решением будет явное определение необходимого члена (без его инициализации): Поместите где-нибудь исходный файл a

template<>
typename B<int>::InitHelper B<int>::mInit;

Это работает в основном так же, как и явное определение класса шаблона.

4
ответ дан 27 November 2019 в 03:52
поделиться
  • [1] раскомментированный случай: Все в порядке. статический InitHelper B :: mInit не существует. Если член класса шаблона (структура) не используется, он не компилируется.

  • [1] и [2] случай без комментариев: Все в порядке. B :: getHelper () использовать статический InitHelper B :: mInit и mInit существует.

  • [1] прокомментировано, [2] без комментариев : у меня это работает в VS2008.

2
ответ дан 27 November 2019 в 03:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: