Понимание шаблонов переменных по примеру

Я пытаюсь понять, как работают шаблоны переменных в следующем примере:

#include <iostream>

template <class T, const T& t>
int var = t.a;

struct T
{
    int a;
    constexpr T(): a(31){ }
};

T y;

const T t = y;

const T tt = T();

int main()
{ 
    std::cout << "var <T, t> = " << var<T, t> << std::endl;  //0
    std::cout << "y.a = " << y.a << std::endl;  //31
    std::cout <<"var <T, tt> = " << var<T, tt> << std::endl; //31
}

DEMO

Честно говоря, я действительно не знаю об этом поведении. Меня смутило то, что специализация var<T, t> равна 0, но y.a - 31. Кроме того, если мы инициализируем объект типа T временным, мы также имеем разные результаты. Не могли бы вы немного уточнить это?

Я имею в виду, я ищу нормативную ссылку из рабочего проекта N4296, описывая это поведение.

Ответы

Ответ 1

В настоящий момент переменные шаблоны довольно неопределенны. Если мы перейдем к текущему списку основных проблем, мы увидим, что

Также было непонятно, какие шаблоны переменных порядка инициализации следуют. CWG issue 1744 изменен [basic.start.init]/p2, чтобы уточнить, что

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

var<T, t> - нелокальная переменная со статической продолжительностью хранения, которая является неявно созданной специализацией. Поэтому его динамическая инициализация неупорядочена. Поскольку t не подходит для постоянной инициализации, это означает, что var<T, t> может быть инициализирован до динамической инициализации t, в результате чего получается 0 независимо от относительного порядка между определением var и t определение и независимо от точки инстанцирования var<T, t>.

Таким образом, переводит определение var ниже определения t и/или явное инстанцирование var<T, t> не влияет на то, что печатается, а , предоставляя явную специализацию для var<T, t>, все еще инициализируя ее до t.a, приводит к тому, что первая строка print 31.

Ответ 2

Причиной является порядок инициализации:

  • Сначала инициализация нуля. Все три переменные установлены на ноль.

  • Затем выполняется постоянная инициализация. y и tt инициализируются с помощью constexpr, который дает 31.

  • Динамическая инициализация - последняя. На этом этапе важны порядок переменных в модуле компиляции. var до t и поэтому var<T, t> инициализируется из t, прежде чем t инициализируется из y.

Ответ 3

Предположительно, специализации var динамически инициализируются перед другими глобальными переменными. В этом случае t требуется динамическая инициализация (поскольку инициализатор не является константным выражением), поэтому при инициализации var<T, t> все равно имеет нулевое значение; в то время как tt может быть статически инициализирован из его инициализатора constexpr, поэтому имеет конечное значение при инициализации var<T, tt>.

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