The result_of Class Template






6.4. The result_of Class Template

template<class Ty> struct result_of {
  typedef T1 type;
  };

One problem that you often run into when writing call wrapper types is that you need to figure out the return type of a call to the target object. The target object, as we've seen, can be a pointer to function, a pointer to a member function, a pointer to member data, or an object with at least one function call operator. The type of the value you get from a pointer to member data depends on the type of the object that it's being applied to, [7] and the return type of an overloaded function call operator can depend on which overload is selected, which, in turn, depends on the arguments that are passed to it. Having to figure out the return type of one of these objects is tedious and error prone. TR1 provides a template, result_of, that gives you a uniform way of getting this information.

[7] That is, its const and volatile qualifiers are determined in part by the type of the object.

The template takes a single type argument that contains the type of a callable object and the list of argument types. This is done by using the syntax of a function type (discussed in Chapter 9). That is, the template argument consists of the callable type followed by a left parenthesis followed by a possibly empty list of argument types followed by a right parenthesis.[8] (As we'll see later, a pointer to member is treated as a function whose first argument designates the object that the member function will be applied to, so the argument list for a pointer to member always has at least one argument type.) The resulting template specialization has a nested type named type that is a synonym for the return type of the template argument.

[8] Since it does not describe a function returning the callable type, the template argument is not, in fact, a function type. The compiler doesn't care, though, so smuggling in the callable type in the guise of a return type works.

Figure. Class Template result_of (funobjover/resultof.cpp)

#include <functional>
#include <math .h>
#include <iostream>
#include <typeinfo>
using std::tr1::result_of;
using std::ostream; using std::cout;

class C
  { // sample class
public:
  C(int i0) : i(i0) {}
  long get() const { return i; }
  int i;
  void operator()(int ii) { i = ii; }
  typedef void result_type;
  };

template <class Fty,class Arg>
void show_return(Fty fun,Arg arg)
  {   // show return type of fun(arg)
  typedef typename result_of<Fty(Arg)>::type ret;
  cout << "Return type of " << typeid(Fty).name()
    << " when called with " << typeid(Arg).name()
    << " is " << typeid(ret).name() << '\n';
  }

int main()
  { // demonstrate class template result_of
  C c(1);
  C *cp = &c;
  const C *ccp = &c;
  show_return(cosf, 1.0);                // cosf(float) returns float
  show_return(&C::get, cp);              // C::get() returns long
  show_return(&C::i, ccp);               // C::ihas type const int
  show_return(c, 3);                     // C() returns void
  return 0;
  }

Depending on which compiler you use, you may have to decipher the type names produced by this program; some compilers give rather cryptic names. In any event, the four calls to show_return should all display the correct return types for the four call wrapper types.

More formally, the nested type named by result_of<F(T1,T2,...,TN)>::type is the return type of the expression f(t1,t2,...,tN), where f is an object of type F, and t1, t2, ..., tN are objects of type T1, T2, ..., TN, respectively. When any of the types Ti is a reference, the corresponding object ti will be an lvalue; otherwise, ti will be an rvalue.

Most of the time when you need to use this template you'll be inside the code of some other template, and the callable type will come in as a template argument. Occasionally, though, you may have to write out the declaration of the callable type and the argument list yourself. This can be confusing, because some callable types have their own argument type list, and you end up with two lists. If you have to write out the full argument list in a case like that, use a typedef for the callable type:[9]

[9] If you really want to know, when you have a pointer to function, the argument type list goes immediately after the *:result_of<int(*(float))(double)>::type.

typedef int (*func)(doubl);            // func is the callable type
result_of<func(float)>::type           // ...

Unfortunately, result_of can't be implemented in portable C++ code. It needs help from the compiler to figure out the return types of function call operators, because there is no way to examine the declaration of the function call operator to determine its return type.[10] Since it is not part of the C++ standard, TR1 ought to be implementable without that sort of help, so it has a list of rules that the implementation should follow if it can't get the return type exactly right. Since TR1 permits this behavior, portable code that uses TR1 should not rely on getting the exact type but should assume that these rules will be applied.

[10] This also means that you can't use result_of to determine which of several overloaded versions of operator() will be called.

For a callable type F and a set of argument types T1, T2, ..., TN, the type result_of<F(T1, T2, ..., TN)>::type is determined as follows.

  • If the type F is a function object defined in the standard library, the nested type type is a synonym for the return type of the call f(t1, t2, ..., tN).

  • If the type F is a pointer to function or a function type, the nested type type is a synonym for its return type.

  • If the type F is a pointer to member function, the nested type type is a synonym for its return type.

  • If the type F is a pointer to data member of a class Ty, the nested type type is a synonym for cv R&, where R is the declared type of F, and cv represents the const and volatile qualifiers of the Ty object referred to by t1.

  • If the type F is a class that has a member named result_type that names a type, the nested type type is a synonym for F::result_-type.[11]

    [11] This requirement is not in TR1, so an implementation that conforms to the TR1 spec-ification does not have to satisfy it. It was accidentally left out and will be added in the future.

  • If the type F is a class that does not have a member named result_-type or that has a member named result_type that does not name a type:

    - If the argument list is empty (N is 0) the nested type type is a synonym for void.

    - Otherwise, the nested type type is a synonym for typename F::result<F(T1, T2, ..., TN)>::type.[12]

    [12] That is, if F defines a nested template named result, result_of uses that template; if F doesn't define that template, it's an error.

  • Otherwise, the program is ill-formed.

These rules are complicated because they try to accommodate existing practice. For the most part, you shouldn't need this much detail. For ordinary functions and pointers to members, result_of gets the return type right. When you write a class with a function call operator, include a member typedef named result_type that names the return type of the function call operator. Don't overload function call operators.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows