Edit: The short answer to my question is that I had a mistaken view of what SFINAE can do and it does not check the function body at all: does sfinae instan
I don't have the time to check this now, but you can add an specialization of Final
: template <typename T> struct Final< Inner<T> >;
(which also helps ensure that the type is always a Inner
. With that you can extract the type used to instantiate Inter
.
Now the second problem is how to use SFINAE to detect whether a member function exists. I believe this should not be too complex (if you don't need to make this generic):
// Find out whether U has `void f()` member
template <typename U>
struct has_member_f {
typedef char yes;
struct no { char _[2]; };
template<typename T, void (T::*)() = &T::f>
static yes impl( T* );
static no impl(...);
enum { value = sizeof( impl( static_cast<U*>(0) ) ) == sizeof(yes) };
};
You might be able to extend this a bit to make it a bit more generic, but the name of the function I don't think you can make generic. Of course, you could write that as a macro that generates has_member_##arg
and uses &T:: arg
. The type of the member is probably easier to generalize...
Alternatively, since I don't think this can be made generic, you can use the trick inside has_member
directly in your type: provide two callFuncA
overloads, one templated with the optional second argument with the signature that you want and defaulted to &T::FuncA
that forwards the call, the other with ellipsis that is a noop. Then callFuncs
would call callFuncA
and callFuncB
, and SFINAE will dispatch to either the forwarder or the noon and you get your desired behavior.
template<typename T>
struct Final< Inter<T> >
{
template <typename U, void (U::*)() = &U::FuncA>
void callFuncA( Inter<T>* x ) {
x.FuncA();
}
void callFuncA(...) {}
void CallFuncs() {
callFuncA(&t); // Cannot pass nonPOD types through ...
// Similarly TryCallFuncB(t);
}
Inter<T> t;
};