I have been trying both approaches, Cubbi's and gunner's.
First, gunner's approach:
He has used a function template get_Tf_result<>() rather than a class template get_Tf_result<>.
I have used this, (after modifying it to use a reference rather than a pointer:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
template<typename T>
auto get_Tf_result(T& t) -> decltype(t.f())
{
return t.f();
}
/** The ellipsis has the lowest conversion rank,
so this overload is chosen only if
the above template overload fails. */
substitution_failure get_Tf_result(...)
{
return substitution_failure {};
}
|
It is tested below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
/// Test cases ...
struct A
{
int f()
{
return 1;
}
} a;
struct B
{
} b;
struct C
{
double f()
{
return 1.1;
}
} c;
void test()
{
cout << "get_Tf_result(a) returns type: "
<< typeid(get_Tf_result(a)).name()
<< endl;
cout << "get_Tf_result(b) returns type: "
<< typeid(get_Tf_result(b)).name()
<< endl;
cout << "get_Tf_result(c) returns type: "
<< typeid(get_Tf_result(c)).name()
<< endl;
}
|
The function works correctly. It discriminates correctly between (1) types containing a function f (such as A and C), returning f()'s return type and (2) types that don't contain a function f (such as B), returning substitution_failure.
Unfortunately, the problem with this approach is that I can't plug this function template into the has_f<> function template which expects the get_Tf_result<> template to contain a member named type. Obviously a function template can't contain such a member.
Therefore, I tried Cubbi's approach. Unfortunately, it doesn't discriminate between types that do and don't contain a function f() and instead returns substitution_failure in both cases.
I tried to analyze this using the following code in C::B:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
struct failure
{
};
template<typename U>
static auto check() -> decltype(std::declval<U>().f())
{
}
static failure check(...)
{
return failure {};
}
struct A
{
int f()
{
return 1;
}
};
int main()
{
cout << typeid(check<A>()).name() << endl;
return 0;
}
|
Perhaps the following compilation error throws some light:
error: static assertion failed: declval() must not be used!|
|
C::B displays the <type_traits> header with the following code highlighted:
1 2 3 4 5 6 7 8
|
template<typename _Tp>
inline typename add_rvalue_reference<_Tp>::type
declval() noexcept
{
static_assert(__declval_protector<_Tp>::__stop,
"declval() must not be used!");
return __declval_protector<_Tp>::__delegate();
}
|
Any advice?
Cubbi had asked which book to refer for Metaprogramming. The original has_f<> example is taken from Stroustrup's "The C++ Prog Lang", 4th Ed, Ch 28 (Metaprogramming).
Thanks.