Giorgio
Giorgio

Reputation: 5173

Function objects in C++ (C++11)

I am reading about boost::function and I am a bit confused about its use and its relation to other C++ constructs or terms I have found in the documentation, e.g. here.

In the context of C++ (C++11), what is the difference between an instance of boost::function, a function object, a functor, and a lambda expression? When should one use which construct? For example, when should I wrap a function object in a boost::function instead of using the object directly?

Are all the above C++ constructs different ways to implement what in functional languages is called a closure (a function, possibly containing captured variables, that can be passed around as a value and invoked by other functions)?

Upvotes: 7

Views: 2846

Answers (3)

pmr
pmr

Reputation: 59811

Function Objects and Functors are often described in terms of a concept. That means they describe a set of requirements of a type. A lot of things in respect to Functors changed in C++11 and the new concept is called Callable. An object o of callable type is an object where (essentially) the expression o(ARGS) is true. Examples for Callable are

int f() {return 23;}

struct FO {
  int operator()() const {return 23;}
};

Often some requirements on the return type of the Callable are added too. You use a Callable like this:

template<typename Callable>
int call(Callable c) {
  return c();
}

call(&f);
call(FO());

Constructs like above require you to know the exact type at compile-time. This is not always possible and this is where std::function comes in.

std::function is such a Callable, but it allows you to erase the actual type you are calling (e.g. your function accepting a callable is not a template anymore). Still calling a function requires you to know its arguments and return type, thus those have to be specified as template arguments to std::function.

You would use it like this:

int call(std::function<int()> c) {
  return c();
}

call(&f);
call(FO());

You need to remember that using std::function can have an impact on performance and you should only use it, when you are sure you need it. In almost all other cases a template solves your problem.

Upvotes: 3

Kerrek SB
Kerrek SB

Reputation: 476990

Both boost::function and the standard version std::function are wrappers provided by the li­brary. They're potentially expensive and pretty heavy, and you should only use them if you actually need a collection of heterogeneous, callable entities. As long as you only need one callable entity at a time, you are much better off using auto or templates.

Here's an example:

std::vector<std::function<int(int, int)>> v;

v.push_back(some_free_function);           // free function
v.push_back(&Foo::mem_fun, &x, _1, _2);    // member function bound to an object
v.push_back([&](int a, int b) -> int { return a + m[b]; });  // closure

int res = 0;
for (auto & f : v) { res += f(1, 2); }

Here's a counter-example:

template <typename F>
int apply(F && f)
{
    return std::forward<F>(f)(1, 2);
}

In this case, it would have been entirely gratuitous to declare apply like this:

int apply(std::function<int(int,int)>)   // wasteful

The conversion is unnecessary, and the templated version can match the actual (often unknowable) type, for example of the bind expression or the lambda expression.

Upvotes: 6

bames53
bames53

Reputation: 88155

A function object and a functor are the same thing; an object that implements the function call operator operator(). A lambda expression produces a function object. Objects with the type of some specialization of boost::function/std::function are also function objects.

Lambda are special in that lambda expressions have an anonymous and unique type, and are a convenient way to create a functor inline.

boost::function/std::function is special in that it turns any callable entity into a functor with a type that depends only on the signature of the callable entity. For example, lambda expressions each have a unique type, so it's difficult to pass them around non-generic code. If you create an std::function from a lambda then you can easily pass around the wrapped lambda.

Upvotes: 6

Related Questions