result_of
Some ramblings about result_of, a little utility available from Boost. It was proposed for standardisation in n1454, was included in TR1, and will be in C++0x. It's used inside some other Boost libraries, but many C++ programmers will never need to use it directly, or know any of what follows. If you do need to know about it, there's a much better description in Pete Becker's book.
The syntax, e.g. result_of<Func(Arg1*,
Arg2&)>::type
may not obvious at first glance,
even if you know C++ fairly well. Result_of is a unary metafunction,
a template with one
parameter:
template<typename Signature>
struct result_of;
There is no definition for the primary template, only for
partial specializations where the template parameter is a
function type:
template<typename Fn, typename... ArgTypes>
struct result_of<Fn(ArgTypes...)>
{
typedef ??? type;
};
The specialization contains a typedef, type
.
In the earlier example, the function type used as the
template argument to result_of is Func(Arg1*,
Arg2&)
That type is a function taking a pointer to Arg1 and an lvalue-reference to Arg2, and returning Func. Compare it to the more familiar declaration of a pointer to such a function:
Func (*)(Arg1*, Arg2&)
This doesn't mean there is a function taking those
arguments and returning Func, it's just a made-up type
passed to result_of, which extracts the necessary
information from the type using a bit of template
metaprogramming from the school of Boost. The function-type
syntax is used to mimic the syntax of invoking some callable
type, Func
, with arguments of those types, as in
Func func;
Arg1 arg1;
Arg2 arg2;
func(&arg1, arg2); // compare with Func(Arg1*,Arg2&);
So result_of<Func(Arg1*,Arg2&)>::type
is a
typedef for
the return type of the call above, which due to overloading
and implicit conversions could be pretty much
anything!
// type is int if Func is:
typedef int (*Func)(Arg1*,const Arg&);
// type is char if Func is:
struct Func {
void operator()(int);
char operator()(void*, Arg2) const;
};
// type is double given:
struct Base { };
struct Arg1 : Base { };
struct Arg2 : Base { };
struct Functor {
int operator()(Arg1*, Arg2&);
double operator()(Base*, Base&, Base* = 0) const;
};
typedef const Functor Func;
In the last example the first operator, which matches the
argument types exactly, is not const, so cannot be called on
a const Functor
, so the second operator is
chosen, converting the arguments and using the default for
the third argument. Getting result_of to give the right
answer was a fun little exercise.