14

In some cases std::function can replace inheritance. The following two code snippets are very similar (about the same costs when calling the function, almost the same usage in signatures and in most cases std::function does not require us to make an extra copy of A as well):

struct Function
{
    virtual int operator()( int ) const =0;
};
struct A
    : public Function
{
    int operator()( int x ) const override { return x; }
};

Using std::function, we can rewrite this as

using Function = std::function<int (int)>;
struct A
{
    int operator()( int x ) const { return x; }
};

In order to make more clear, how the two snippets are related: both of them can be used in the following way:

int anotherFunction( Function const& f, int x ) { return f( x ) + f( x ); }

int main( int argc, char **argv )
{
    A f;
    anotherFunction( f, 5 );
    return 0;
}

The latter approach is more flexible, because we do not have to derive our classes from some common base class. The only relationship between Function-like objects is based on their capabilities. In terms of object-orientated programming it might be considered less clean (but not in terms of functional programming, of course).

Apart from that, are there any other differences between the two solutions? Are there any general guidelines when to use which of the solutions or is it only a matter of personal preference? Are there any cases where one solution out-performs the other?

Markus Mayr
  • 3,568
  • 16
  • 38
  • 3
    Maybe I'm just thick, but I can't see how the two examples are related. – jrok May 08 '13 at 08:04
  • 2
    IMO you should always use either `std::function`, or the usual template approach `template` as they're idiomatic. Rolling your own `Function` type won't work for *all* callable types. – Pubby May 08 '13 at 08:09
  • 1
    `std::function` is C++ish. So I would choose this, or would use `template` (class templates and function templates). – Nawaz May 08 '13 at 08:09

1 Answers1

21

A small correction first: notice, that this:

int anotherFunction( Function f, int x ) { return f( x ) + f( x ); }

Would not compile with the inheritance-based solution, since Function is being taken by value and it is abstract. If it were not abstract, on the other hand, you would get slicing, which is not what you want.

Rather, you would have to take your Function-derived object by reference (possibly by reference to const) in order to take advantage of polymorphism:

int anotherFunction( Function const& f, int x ) { return f( x ) + f( x ); }

And this is not very functional-like, so if you're keen on functional programming (as you seem to be) you may want to avoid it just because of this.


That said, here is the guideline I would provide:

  1. If you can, use templates:

    template<typename F>
    int anotherFunction(F f, int x) { return f(x) + f(x); }
    

    In general, when it can be used at all, static (compile-time, template-based) polymorphism is considered preferable to dynamic (run-time, inheritance-based) polymorphism, because of:

    • Superior flexibility: you do not have to change the definition of your types and make them derive from a common base class in order for them to be used generically. This allows you, for instance, to write:

      anotherFunction([] (int x) { return x * 2; }, 42);
      

      As well as:

      anotherFunction(myFxn, 42); // myFxn is a regular function
      

      Or even:

      anotherFunction(my_functor(), 42); // my_functor is a class
      
    • Superior performance: since you are not calling through a virtual table and the compiler knows how function calls will be resolved, it can inline the call to give you greater performance (if it deems this to be reasonable).

  2. If you cannot use templates, because the function to be invoked is going to be determined at run-time, use std::function:

     int anotherFunction(std::function<int (int)> f, int x) 
     { 
         return f(x) + f(x); 
     }
    

    This will also give you enough flexibility to pass in lambdas, function pointers, functors, basically any callable object. See, for instance, this Q&A on StackOverflow.

    Using std::function may bring a significant run-time overhead with respect to a template-based design, and perhaps also a slight minor overhead with respect to a hardcoded inheritance-based solution like the one you outline, but it gives you flexibility and it is a standard idiom. Moreover, as always when performance is a concern, make measurements to back up any assumption - you might get surprising results.

    You usually need to resort to this kind of std::function-based design when you want to store callable objects of any type for later invocation, as in the case of the Command design pattern, or when you have a heterogenous collection of callable objects to be treated and invoked generically. For a discussion on when to use std::function instead of templates, see this Q&A on StackOverflow.

  3. So when should you resort to a hardcoded design using inheritance? Well, in all those cases where 1. and 2. are not viable - honestly, I can't think of any right now, but I'm sure someone could come up with a corner case. Notice, that C++11 is not necessary in order to make use of an std::function-like idiom, since Boost has a boost::function and a boost::bind implementation that pre-date (and served as an inspiration for) C++11's std::function and std::bind.


TL;DR: Use templates or std::function.

Community
  • 1
  • 1
Andy Prowl
  • 114,596
  • 21
  • 355
  • 432
  • 4
    +1 for the mentioning of the usefulness of `std:function` in design patterns. See also this [ACCU 2013 presentation](http://accu.org/content/conf2013/Tobias_Darm_Effective_GoF_Patterns.pdf) – TemplateRex May 08 '13 at 09:19
  • @rhalbersma: That's a very interesting presentation, thank you for sharing it! – Andy Prowl May 08 '13 at 09:26
  • I corrected the error you mentioned; could not do it earlier due to a meeting. In my opinion, 1. has two drawbacks as well: code bloat / code might take longer to compile and less useful error messages. But thanks for the great links and the great answer! – Markus Mayr May 08 '13 at 09:29
  • @MarkusMayr: You're right, templates tend to lead to confusing error messages, but with a bit of practice, most of the time it isn't that bad. Also, Concepts Lite (which we *may* be able to use in a hopefully-not-too-far-away future) should solve this problem. As for code bloat, personally I have never had a reason to be concerned, but I can imagine that on some embedded architectures where code size must be minimized this could have an impact. Thank you for bringing it on – Andy Prowl May 08 '13 at 09:33