Компилируется, потому что так работают руководства по выводу шаблонов классов.
Направляющие вычета являются гипотетическими конструкторами типа. Они на самом деле не существуют. Их единственная цель - определить, как вывести параметры шаблона класса.
Как только вычет сделан, фактический код C ++ вступает во владение с определенным экземпляром test
. Поэтому вместо test t{{1, 2}};
компилятор ведет себя так, как если бы вы сказали test<int> t{{1, 2}};
.
test<int>
имеет конструктор, который принимает pair<int, int>
, который может соответствовать значениям в списке braced-init-list, так что это то, что вызывается.
Такого рода действия были сделаны, чтобы агрегаты могли участвовать в выводе аргументов шаблона класса. Агрегаты не имеют пользовательских конструкторов, поэтому, если руководства по выводам были ограничены только реальными конструкторами, агрегаты не могли работать.
Итак, у нас есть руководство по выводу шаблона класса для std::array
:
template <class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;
Это позволяет std::array arr = {2, 4, 6, 7};
работать. Он выводит как аргумент шаблона, так и длину из руководства, но поскольку руководство не является конструктором, array
остается агрегатом.