I understand that template member functions are only generated if used. This is convenient if not all used types support such a function. However, this does not appear to work for functions with trailing return type specification. Below is a small experiment:
// helper function for case A workaround
template <typename A, typename T>
auto F(T&& x)
-> decltype(x.template f <A>())
{ return x.template f <A>(); }
// helper function for case B workaround
template <typename A, typename T>
auto G(T&& x)
-> decltype(x.g())
{ return x.g(); }
template <typename T>
struct S
{
// case A: not ok in GCC + Clang
template <typename A>
auto f1()
-> decltype(T().template f <A>())
{ return T().template f <A>(); }
// case A workaround: ok in Clang + GCC
template <typename A>
auto f2()
-> decltype(F <A>(T()))
{ return F <A>(T()); }
// case B: ok in GCC, not ok in Clang
template <typename A>
auto g1()
-> decltype(T().g())
{ return T().g(); }
// case B workaround: ok in GCC + Clang
template <typename A>
auto g2()
-> decltype(G <A>(T()))
{ return G <A>(T()); }
};
Please keep in mind that this sample is only meant to illustrate the issue, it is not useful in anything else.
S <T>
can be instantiated for any type T
that has appropriate member functions f
, g
.
However, if I try to instantiate S <int>
, e.g. by S <int> s{};
, I do get errors like type 'int' is not a structure or union
. This happens for both cases f1
, g1
, which attempt to call template function f
or non-template function g
respectively on a value of type T
(int
in this case). It happens even though I am not trying to call f1
or g1
on object s
. However, GCC is fine with the case of g1
; Clang is not.
A workaround for case A (template member function f
) is to use a helper function F
, which is what f2
does, and works fine for both Clang and GCC. It appears to work because call T().template f <A>()
is hidden from the declaration of f2
, and the compiler is not looking into F <A>(T())
when type A
is unknown.
The same workaround for case B (non-template member function g
) is also working for both compilers.
I would appreciate some help in finding out what is going on. Which is the correct behaviour in each case? Which compiler is correct? Is there any other workaround in general?
I am using GCC 4.8.1 and Clang 3.3.
SFINAE only applies to the template arguments of the function, not the one inherited from the class.
A different solution is to include copy T to a second template argument but this is nothing more than a shorter version of your workaround: