
on Tue Apr 29 2008, "Daniel Walker" <daniel.j.walker-AT-gmail.com> wrote:
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.
Ah, but I think you miss the point. I need the specializations to handle all the different CV-qualifications of the *function object*. I suppose if you have full control over the function object you can do it with a nested result<>, and now I forget why I might not have wanted to do that. Perhaps I just didn't want to force F to be instantiated when checking its result 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(); }
Fine, then take the case where call takes f by value; then F can be a function type and result_of<F()> is illegal. Because function call operators have to be member functions in C++03, an lvalue function object must behave the same as an rvalue, so result_of<T()>::type == result_of<T&()>::type is an invariant. That means I can afford to always add a reference, but it's still painful: typename result_of<typename boost::add_reference<F>::type ()>::type
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.
Ouch.
Of course, that opens another can of worms, if you'd like to preserve the polymorphic behavior of overloaded/templated built-ins.
Well you can't handle that case anyway; an overload set or a template doesn't have a type that can be passed on to result_of. -- Dave Abrahams Boost Consulting http://boost-consulting.com