Я подал сообщение о проблеме, которая, по моему мнению, была ошибкой в 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]
Этот сценарий подпадает под критерии, которые я выделил, поэтому он неупорядочен.В конце есть примечание, которое, я думаю, читается как «явно специализированная переменная специализация шаблона имеет упорядоченную инициализацию», но я не полностью (явно) специализируюсь, поэтому она не применима.
Я такжеувидел этот ответ , который, кажется, конфликтует с вышеуказанной частью спецификации:
Глобальные переменные в одной единице перевода (исходный файл) инициализируются в том порядке, в котором ониопределены.
- Правильно ли приведенные выше рассуждения?
- Будет ли
constexpr std::scalbn
решить эту проблему? - Что касается создания кодалегальный / переносимый, MSVC хрипит, когда я пытаюсь явно определить специализацию, добавив
template const MyClass huge<MyClass>
.Притворство использовать специализацию huge<double>
работает, но неиспользуемый код (GCC предупреждает).Я знаю, что вместо этого я могу сделать эти статические локальные переменные в функции (с риском добавления блокировки / защиты) или вернуть значения функций или шаблонов классов, но этот вариант является наиболее кратким, если он допустим.