Clang-3.2 может компилировать и код вести себя как положено:
struct have_f { int f(int i) {return 10;} };
struct empty {};
template <class T>
struct outer {
T t;
// if T have f(), define outer_f()
template<class U=decltype(t.f(1))>
int outer_f(int i) { return t.f(i); }
};
int main() {
outer<have_f> o1;
outer<empty> o2;
// to silence unused var warning
return o1.outer_f(10) + sizeof(o2);
}
GCC любой версии отклоняется с:
t.cc:13:6: error: ‘struct empty’ has no member named ‘f’
int outer_f(int i) { return t.f(i); }
^
Кто прав? Gcc или Clang?
Обратите внимание, что там было аналогичный вопрос, без реального ответа.
Я считаю, что проблема 14.6.3 [temp.nondep]:
1 — Независимые имена, используемые в определении шаблона, находятся с использованием обычного поиска имен и привязываются в той точке, в которой они используются.
В приведенном примере описывается, что неправильно сформированное выражение в определении шаблона «может быть диагностирован либо [в определении шаблона], либо в момент создания».
По умолчанию Шаблон-аргумент (14.1p9) U=decltype(t.f(1))
является независимым именем в контексте реализации struct outer
(то есть, он не зависит от аргумента шаблона к своему собственному шаблону), поэтому он плохо сформирован для реализации struct outer
с T = struct empty
, Стандарт не содержит явного описания того, где оцениваются аргументы шаблона по умолчанию, но единственный разумный вывод заключается в том, что они обрабатываются как любая другая конструкция и оцениваются в точке, в которой они встречаются (или, в этом примере, в точке создания экземпляра struct outer
шаблон). Я не вижу никакой возможности для компилятора задерживать оценку независимых аргументов шаблона по умолчанию для контекстов, в которых применяется SFINAE.
К счастью, решение простое: просто создайте шаблон по умолчанию 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); }
Других решений пока нет …