Порядок динамической инициализации неявно созданных шаблонов переменных - PullRequest
1 голос
/ 05 июня 2019

Я подал сообщение о проблеме, которая, по моему мнению, была ошибкой в ​​MSVC, но, как оказалось, это поведение, определяемое реализацией.Я хочу убедиться, что я полностью понимаю причину.У меня это в одной единице перевода:

#include <limits>
#include <utility>
#include <type_traits>
#include <cmath>
#include <iostream>

struct MyClass {
  double value_of;
  MyClass(double d): value_of(d) {}
};

template<class T> struct MyTraits { static constexpr bool do_enable = false; };
template<> struct MyTraits<MyClass> { static constexpr bool do_enable = true; typedef double value_type; };

template<typename T> using EnableIfFP = std::enable_if_t<std::is_floating_point<T>::value>;
template<typename T> using EnableIfMyClass = std::enable_if_t<MyTraits<T>::do_enable>;

template<typename T> constexpr int EXP = std::numeric_limits<T>::max_exponent / 2;

template<typename T, typename Enabler = void> const T huge = T{ 0 };
template<typename T> const T huge<T, EnableIfFP<T> > = std::scalbn(1.0, EXP<T>);
template<typename T> const T huge<T, EnableIfMyClass<T> > = T{ huge<typename MyTraits<T>::value_type> };

int main() {
  // huge<double>; // PRESENCE OF THIS LINE AFFECTS OUTPUT IN MSVC
  std::cout << huge<MyClass>.value_of << std::endl;
  return 0;
}

https://godbolt.org/z/Iwpwzf

Я ожидал, что huge<MyClass>.value_of будет 1,34078e + 154: 3-е определение huge должно использовать2-е определение для его иници.Clang 7, GCC 8 и ICC 19 все это делают, но MSVC 2017 печатает 0 (либо из T{ 0 }, либо из нулевой инициализации, idk), если строка с комментариями не включена.

Как я теперь понимаю, main() неявно создает экземпляры huge<MyClass> и (косвенно) huge<double>, но их инициализация неупорядочена:

Динамическая инициализация нелокальной переменной со статической продолжительностью хранения неупорядоченный, если переменная является неявно или явно созданной специализацией , является частично упорядоченной, если переменная является встроенной переменной, которая не является неявным или явно созданным экземпляром специализации, и в противном случае упорядочена.[ Примечание : явно специализированный не встроенный статический элемент данных или специализация переменной шаблона имеет упорядоченную инициализацию.- конец примечания] [basic.start.dynamic]

Этот сценарий подпадает под критерии, которые я выделил, поэтому он неупорядочен.В конце есть примечание, которое, я думаю, читается как «явно специализированная переменная специализация шаблона имеет упорядоченную инициализацию», но я не полностью (явно) специализируюсь, поэтому она не применима.

Я такжеувидел этот ответ , который, кажется, конфликтует с вышеуказанной частью спецификации:

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


  1. Правильно ли приведенные выше рассуждения?
  2. Будет ли constexpr std::scalbn решить эту проблему?
  3. Что касается создания кодалегальный / переносимый, MSVC хрипит, когда я пытаюсь явно определить специализацию, добавив template const MyClass huge<MyClass>.Притворство использовать специализацию huge<double> работает, но неиспользуемый код (GCC предупреждает).Я знаю, что вместо этого я могу сделать эти статические локальные переменные в функции (с риском добавления блокировки / защиты) или вернуть значения функций или шаблонов классов, но этот вариант является наиболее кратким, если он допустим.

1 Ответ

1 голос
/ 05 июня 2019
  1. Вы правы - «конфликтующий» ответ полностью предшествует шаблонам переменных.(Утверждение все еще верно, учитывая, что переменная template не является «глобальной переменной» и что ее экземпляры не определены ( т.е. , объявлены) текстуально.)
  2. Да, std::scalbn поможет constexpr - это только динамическая нелокальная инициализация, которая (иногда) неупорядочена, и если частичная специализация переменного шаблона (литерального типа) инициализируется с помощьюконстантное выражение, такой динамической инициализации не происходит.Удобно, что такая совместная очистка была одобрена для C ++ 20, хотя она может или не может пройти проверку формулировок вовремя для этого.
  3. Никакое количество неявных или явных экземпляров здесь не поможет -это может быть только явная специализация, как вы процитировали, или различные приемы скрытия вещей в функциях.Конечно, вы можете использовать функции для инициализации переменных (и всего, что может быть инициализировано до того, как они будут) и использовать переменные везде.
...