Reputation: 62774
Consider the following class:
class MyClass
{
public:
template<class T> typename T::result_type apply(T& func)
{
if (is_int())
{
return func(int(0));
}
return func(double(0));
}
...
};
(The code does not look terribly useful, but it is only a contrived sample to demonstrate my point)
Anyway, a typical functor would be something like this:
struct MyFunc
{
typedef void result_type;
template<class V> void operator()(V)
{
// do something
}
};
And one would use it like so:
MyClass c;
MyFunc f;
c.apply(f);
My question is this - can MyClass::apply
be changed to recognize a slightly different version of the functor in addition to the original one, for instance the one expecting the caller object reference to be passed along with all the other parameters, something like this:
struct MyFuncEx
{
typedef void result_type;
template<class V> void operator()(const MyClass& caller, V)
{
// do something
}
};
So, the following code would compile too:
MyClass c;
MyFunc f;
c.apply(f);
MyFuncEx f2;
c.apply(f2);
As a bonus I would like the compilation to fail if the functor contains both overloads, i.e. the following should fail the compilation:
struct MyFuncSmartAss
{
typedef void result_type;
template<class V> void operator()(V)
{
// do something
}
template<class V> void operator()(const MyClass& caller, V)
{
// do something
}
};
...
MyClass c;
c.apply(MyFuncSmartAss());
But, it is not that important as long as the longer overload takes the precedence over the shorter one.
Upvotes: 3
Views: 142
Reputation: 299910
It really depends whether you have C++11 or not. This should solve the overload issue in C++11 (*):
class MyClass {
private:
int _int;
double _double;
public:
template <typename F>
auto apply(F& f) -> decltype(f(_int), f(_double)) {
if (is_int()) { return f(_int); }
return f(_double);
}
template <typename F>
auto apply(F& f) -> decltype(f(*this, _int), f(*this, _double)) {
if (is_int()) { return f(*this, _double); }
return f(_double)
}
};
How does it work ?
decltype
creates an unevaluated context. The compiler performs regular overload resolution of the expression but only cares about the type. If an error occurs (the operator()
does not exist in f
), then we hit SFINAE and this overload of apply
is discardedF
provides both operators) then the call is ambiguous(*) I am not too sure about the correctness of the trailing-return-type specification, and more specifically the use of this
and _arg
. Both Clang 3.0 and gcc 4.5.2 error out. This can be worked around, just get a bit more verbose.
// first
decltype(f(0), f(0.0))
// second
decltype(f(std::declval<MyClass>(), 0), f(std::declval<MyClass>(), 0.0))
In action at ideone with this workaround:
EDIT: to cope with the int/double
requirement.
Upvotes: 3