Неявное создание экземпляра шаблона класса вызывает только создание экземпляров объявлений, которые он содержит. Определения, как правило, создаются только в том случае, если они используются в контексте, для которого требуется определение.
Таким образом, если вы не используете Template<int>::c1
таким образом, чтобы его определение существовало (т.е. с использованием odr), тогда он вообще не будет определен.
Один из способов использования odr с переменной - это взять ее адрес, как вы упомянули.
Даже если вы форсируете В случае создания экземпляра переменной нет гарантии, когда именно она будет инициализирована. Конструктор
C1
не является constexpr
, поэтому инициализация Nontemlate::c1
не может быть константным выражением. Это означает, что вы получите динамическую c инициализацию Template<int>::c1
. Динамическая c инициализация глобальных переменных stati c, являющихся частью специализации шаблона, неупорядоченная , что означает, что нет никакой гарантии, в каком порядке они произойдут относительно любой другой динамической c инициализации глобальные переменные c.
Точно так же Nontemlate::c2
не инициализируется константным выражением и поэтому также динамически инициализируется. Хотя Nontemlate::c2
имеет частично упорядоченную динамическую c инициализацию (являющуюся переменной inline
, не являющейся частью специализации шаблона), она все еще неопределенно упорядочена относительно Template<int>::c1
, как объяснено выше.
Также строго не требуется, чтобы Template<int>::c1
и Nontemlate::c2
были инициализированы до ввода main
. Это определяется реализацией, будет ли инициализация отложена до более позднего периода, но до первого использования соответствующей переменной. Я думаю, что эта отсрочка используется в основном для динамической загрузки библиотеки c во время выполнения.
Распространенный метод, позволяющий избежать проблем с упорядочением глобальных переменных продолжительности хранения stati c, заключается в использовании функции, возвращающей ссылку на вместо этого локальная переменная stati c, то есть:
template<typename T>
struct Template {
static auto& c1() {
static C1 instance;
return instance;
}
};
, хотя при частых вызовах это может повлиять на производительность, поскольку каждый раз необходимо проверять структуру локального stati c.
Альтернативно, если инициализатор можно сделать постоянным выражением, создание переменной constexpr
должно гарантировать постоянную инициализацию , что означает, что никакой инициализации динамически c не произойдет вообще и не возникнет проблем с упорядочением. .