Я не понимаю, как это неоднозначно - A является декларацией, а B
такое определение функции, объявленной А. Верно?
Нет. A - это объявление шаблона функции, а B - это объявление (и определение) другого шаблона функции.
Компилятор не может выбирать между двумя: у них обоих нет аргументов, а аргументы шаблона совпадают для обоих.
То, что в середине, является явной полной специализацией шаблона функции, объявленного в A.
Если вы пытались сделать B другой специализацией A:
template <typename H, typename... T> /* B */
size_t num_args<H, T...>()
{
return 1 + num_args <T...> ();
}
... в итоге вы получите частичную специализацию шаблона функции, что недопустимо.
Вы можете сделать это с помощью обычного приема использования шаблона класса с частичной специализацией и шаблона функции, который вызывает шаблон класса:
template <typename... T>
class Num_Args;
template <>
struct Num_Args <>
{
static constexpr size_t calculate() {
return 0;
}
};
template <typename H, typename... T>
struct Num_Args <H, T...>
{
static constexpr size_t calculate() {
return 1 + Num_Args<T...>::calculate();
}
};
template <typename... T> /* B */
constexpr size_t num_args ()
{
return Num_Args<T...>::calculate();
}