Ответ 1
Я считаю, что проблема 14.6.3 [temp.nondep]:
1 - Не зависящие от имени имена, используемые в определении шаблона, определяются обычным поиском имен и связаны в той точке, в которой они используются.
В приведенном примере описывается, что плохо сформированное выражение в определении шаблона "может быть диагностировано либо [в определении шаблона], либо в момент создания экземпляра".
Шаблон-аргумент по умолчанию (14.1p9) U=decltype(t.f(1))
является необязательным именем в контексте создания struct outer
(то есть он не зависит от аргумента шаблона к его собственному шаблону), поэтому он плохо сформирован для экземпляра struct outer
с T = struct empty
. В стандарте явно не указано, где оцениваются аргументы шаблона по умолчанию, но единственным разумным заключением является то, что они рассматриваются как любая другая конструкция и оцениваются в той точке, в которой они происходят (или, в этом примере, в момент создания struct outer
). Я не вижу никакой широты для компилятора, чтобы отложить оценку необязательных аргументов шаблона по умолчанию в контекстах, где применяется SFINAE.
К счастью, решение легко: просто сделайте аргумент template-argument по умолчанию U
зависимым именем:
// if T have f(), define outer_f()
template<class T2 = T, class U=decltype(static_cast<T2 &>(t).f(1))>
int outer_f(int i) { return t.f(i); }