Can we get the type of a lambda argument?

It’s not desirable in the general case. (Note that it’s quite easy for std::function<T(A)> to specify what e.g. argument_type is: it’s just A! It’s available in the type definition.)

It would be possible to require each and every function object type to specify its argument types, and in turn mandate that the closure types generated from lambda expression do so. In fact, pre-C++0x features like adaptable functors would only work for such types.

However, we’re moving from that with C++0x and for good reasons. The simplest of which is simply overloading: a functor type with a templated operator() (a.k.a a polymorphic functor) simply takes all kind of arguments; so what should argument_type be? Another reason is that generic code (usually) attempts to specify the least constraints on the types and objects it operates on in order to more easily be (re)used.

In other words, generic code is not really interested that given Functor f, typename Functor::argument be int. It’s much more interesting to know that f(0) is an acceptable expression. For this C++0x gives tools such as decltype and std::declval (conveniently packaging the two inside std::result_of).

The way I see it you have two choices: require that all functors passed to your template use a C++03-style convention of specifying an argument_type and the like; use the technique below; or redesign. I’d recommend the last option but it’s your call since I don’t know what your codebase looks like or what your requirements are.


For a monomorphic functor type (i.e. no overloading), it is possible to inspect the operator() member. This works for the closure types of lambda expressions.

So we declare these helpers

template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...));

template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...) const);

// volatile or lvalue/rvalue *this not required for lambdas (phew)

that accept a pointer to member function taking at least one argument. And now:

template<typename F>
struct first_argument {
    typedef decltype( helper(&F::operator()) ) type;
};

[ an elaborate trait could successively query the lvalue-rvalue/const/volatile overloads and expose the first argument if it’s the same for all overloads, or use std::common_type.]

Leave a Comment