Ответ 1
[temp.over.link]/6 указывает, когда две объявления шаблона функций являются перегрузками. Это делается путем определения эквивалентности двух шаблонов функций следующим образом:
Два шаблона функций эквивалентны, если они [..] имеют возвращаемые типы [..], которые эквивалентны с использованием правил описанных выше, для сравнения выражений с параметрами шаблона.
"Правила, описанные выше" - это
Рассматриваются два выражения, включающие параметры шаблона эквивалентно, если два определения функций, содержащие выражения, будут удовлетворять одному правилу определения (3.2) [..]
ODR, релевантный для этой части, указывает [basic.def.odr]/6, что
Учитывая такой объект с именем
D
, определенный более чем в одном переводе единица, затем
- каждое определение
D
должно состоять из одной и той же последовательности токенов;
Очевидно, что поскольку типы возвращаемых данных (которые являются возвращаемыми типами возврата в [dcl.fct]/2), не состоят из те же токены, два определения функций, содержащие эти выражения, будут нарушать ODR.
Следовательно, объявления foo
объявляют неэквивалентные шаблоны функций и перегружают имя.
Ошибка, которую вы видите, выдается из-за отсутствия поддержки со стороны VС++ для выражения SFINAE - предположительно, возвращаемые типы трейлинга не проверяются на эквивалентность.
Обход
Вы можете сделать шаблоны функций неэквивалентными по-другому. Измените список параметров шаблона. Если вы перепишете второе определение так:
template <typename F, int=0>
auto foo(F&& f) -> decltype(f(0, 1), void())
{
std::cout << "2" << std::endl;
}
Затем VС++ компилирует его в порядке. Я сократил цитату в [temp.over.link]/6, которая охватывает это:
Два шаблона функций эквивалентны, если они объявлены в одном и том же scope, имеют одинаковое имя, имеют одинаковые списки параметров шаблона [..]
Фактически, чтобы иметь возможность легко вводить новые перегрузки, вы можете использовать небольшой помощник:
template <int I>
using overload = std::integral_constant<int, I>*;
Использование, например,
// Remember to separate > and = with whitespace
template <typename... F, overload<0> = nullptr>
auto foo(F&&... f) -> decltype(f(0, 1)..., void())
template <typename... F, overload<1> = nullptr>
auto foo(F&&... f) -> decltype(f(0, 1, 2)..., void())
Демо.