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.
The trailing return type feature removes a C++ limitation where the return type of a function template cannot be generalized if the return type depends on the types of the function arguments.
In C++14, you can just use auto as a return type.
Which member function doesn't require any return type? Explanation: All the member functions work same as normal functions with syntax. But the constructor and destructor are also considered as member functions of a class, and they never have any data type.
To instantiate a template function explicitly, follow the template keyword by a declaration (not definition) for the function, with the function identifier followed by the template arguments. template float twice<float>(float original); Template arguments may be omitted when the compiler can infer them.
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:
#include <utility>
#include <type_traits>
struct Foo {
template < typename T > T f() { return {}; }
};
template <typename T>
struct S {
template <typename A, typename TT = T >
auto f1() -> decltype(std::declval<TT>().template f <A>()) {
static_assert(std::is_same<T,TT>::value, "TT must be equal to T" );
return TT().template f <A>();
}
};
int main() {
S<Foo> a;
a.f1<int>(); // ok
S<int> b;
b.f1<int>(); // not ok
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With