Элементы-члены шаблона с типом возвращаемого типа, дающие ошибки, даже если они не используются

Я понимаю, что функции члена шаблона генерируются только в том случае, если они используются. Это удобно, если не все используемые типы поддерживают такую ​​функцию. Однако это не работает для функций с конечной спецификацией возвращаемого типа. Ниже представлен небольшой эксперимент:

// helper function for case A workaround template  auto F(T&& x) -> decltype(x.template f ()) { return x.template f (); } // helper function for case B workaround template  auto G(T&& x) -> decltype(xg()) { return xg(); } template  struct S { // case A: not ok in GCC + Clang template  auto f1() -> decltype(T().template f ()) { return T().template f (); } // case A workaround: ok in Clang + GCC template  auto f2() -> decltype(F (T())) { return F (T()); } // case B: ok in GCC, not ok in Clang template  auto g1() -> decltype(T().g()) { return T().g(); } // case B workaround: ok in GCC + Clang template  auto g2() -> decltype(G (T())) { return G (T()); } }; 

Пожалуйста, имейте в виду, что этот пример предназначен только для иллюстрации проблемы, он ничем не полезен.

S может быть создан для любого типа T который имеет соответствующие функции-члены f , g .

Однако, если я пытаюсь создать экземпляр S , например, S s{}; , Я получаю ошибки, такие как type 'int' is not a structure or union . Это происходит для обоих случаев f1 , g1 , которые пытаются вызвать функцию шаблона f или функцию без шаблона g соответственно для значения типа T ( int в этом случае). Это происходит, хотя я не пытаюсь вызвать f1 или g1 на объекте s . Однако GCC в порядке с g1 ; Клэнг – нет.

Обходной путь для случая A (функция члена-члена f ) заключается в использовании вспомогательной функции F , что и делает f2 , и отлично работает как для Clang, так и для GCC. Кажется, что это работает, потому что вызов T().template f () скрыт от объявления f2 , и компилятор не ищет F (T()) когда тип A неизвестен.

То же обходное решение для случая B (функция нечленов-членов g ) также работает для обоих компиляторов.

Я был бы признателен за помощь в выяснении того, что происходит. Какое правильное поведение в каждом случае? Какой компилятор прав? Есть ли другой способ обхода?

Я использую GCC 4.8.1 и Clang 3.3.

    SFINAE применяется только к аргументам шаблона функции, а не к унаследованным от classа.

    Другое решение состоит в том, чтобы включить копию T во второй аргумент шаблона, но это не более чем короткая версия вашего обходного пути:

     #include  #include  struct Foo { template < typename T > T f() { return {}; } }; template  struct S { template  auto f1() -> decltype(std::declval().template f ()) { static_assert(std::is_same::value, "TT must be equal to T" ); return TT().template f (); } }; int main() { S a; a.f1(); // ok S b; b.f1(); // not ok }