
On Tue, Apr 29, 2008 at 5:43 AM, David Abrahams <dave@boost-consulting.com> wrote:
The difficulty of implementing the result_of protocol has always bothered me. I've often found myself writing a primary result<...> template and several specializations to handle different cv-qualifications, and I've still not felt that I was doing it quite right.
Yes, I think that's the right way to do it. My understanding is that every time you write a new overload, you need to write a new specialization of result<> for that overload, whether you're overloading on arity, type, cv-qualification, etc. You have to advertise the return type of each function definition that the compiler could choose during overload resolution; i.e there's always a one-to-one correspondence between result<> specializations and function overloads. So, there's one more regular step to take when defining a function overload - whenever you define a return type in the overload's prototype, also advertise that return type by specializing result<> for the overload. I guess this can become habitual, but like all manual processes, I can see how this poses an opportunity for human error. The result_of<> documentation doesn't address these use-cases at all, so that's no help. Maybe Egg could be of help, but it may have some usability issues of its own. Of course, the ultimate solution is the C++0x result_of that works by fiat/decltype. <snip>
template <class F> typename result_of<F()>::type call(F const& f) { return f(); }
int f() { return 0; }
int x = call(f);
The problem is that in the above case, F is deduced as int(), and we form an illegal function type (one that itself returns a function type) in result_of<F()>::type.
<snip>
Is there an easier way?
Yes, I believe so. The result_of documentation says that for a callable object f of type F you have result_of<F()>::type. So, I think one should use the exact type in situations like this. In your example above, f is a F const&, so... template <class F> typename result_of<F const&()>::type call(F const& f) { return f(); } I tried this with gcc 4.3, and it works with your example. However, I don't think this is a perfect approach for passing arbitrary callable objects. I confess I don't know all the issues off the top of my head, but I believe a better approach is to have call() accept only full-blown, first-class function objects as arguments. If users would like to pass built-in functions, they should first promote them to first-class function objects using boost::function/std::function. Of course, that opens another can of worms, if you'd like to preserve the polymorphic behavior of overloaded/templated built-ins. Daniel Walker