Однако я не понимаю, почему второй компилируется, а другие не [компилируют].
Примеры 3 и 4 не компилируются, поскольку выведенные аргументы шаблона отличаются отчто вы ожидаете.
Когда вы записываете вызов (f({5, 3})
) в шаблон функции template<typename ...As> void f(Foo<As...>)
, компилятор должен вывести все пропущенные аргументы шаблона (...As
).Эти отсутствующие аргументы шаблона сначала выводятся путем сравнения аргументов функции ({5, 3}
) с параметрами функции (Foo<As...>
).Если аргумент функции является списком инициализатора ({5, 3}
) [1], вывод аргумента шаблона пропускается для параметра функции (Foo<As...>
):
N4659 [temp.deduct.call] 17.8.2.1 (1):
[...] аргумент списка инициализатора приводит к тому, что параметр считается недоступным контекстом (17.8.2.5).
N4659 [temp.deduct.type] 17.8.2.5 (5.6):
[Неведедированный контекст включает в себя] параметр функции, для которого связанный аргумент является списком инициализатора (11.6.4) [...]
Поскольку пакет параметров конечного шаблона (As...
) не выводится никакими аргументами функции, он выводится как пустой:
N4659 [temp.arg.explicit] 17.8.1 (3):
Пакет параметров конечного шаблона (17.5.3), не выведенный иным образом, будет выведен в пустую последовательность аргументов шаблона.
Для f({5, 3})
компилятор выводит, что аргументы шаблона <>
(пустые), таким образом специализация вызываетсяd составляет void f(Foo<>)
.Важно: эта специализация выбирается независимо от того, что находится внутри списка инициализатора.Это объясняет диагностику, предоставленную компилятором:
Clang:
ошибка: нет соответствующей функции для вызова 'f'
примечание: функция-кандидат [с As = <>] не жизнеспособна: невозможно преобразовать аргумент списка инициализатора в 'Foo <>'
GCC:
ошибка: не удалось преобразовать '{5, 3}' из '' в 'Foo <>'
MSVC:
ошибка C2664: «void f <> (Foo <>)»: невозможно преобразовать аргумент 1 из «списка инициализаторов» в «Foo <>»
примечание: ни один конструктор не может принять тип источника или конструкторразрешение перегрузки было неоднозначным
При вызове функции параметры функции инициализируются с использованием copy-initialization из аргументов функции.Следующие операторы инициализируют переменную с использованием тех же правил, что и ваши примеры инициализируют параметр f
:
Foo<> x = {}; // Example 2
Foo<> x = {5, 3}; // Example 3
Foo<> x = {5, 3.8}; // Example 4
[1] и, вкратце, если параметр функции не является ни std::initializer_list<T>
, ни T[n]