17

I am playing around with the c++11 functional features. One thing I find odd is that the type of a lambda function is actually NOT a function<> type. What's more, lambda's do not seem to play really well with the type-inferencing mechanism.

Attached is a small example in which I tested flipping the two arguments of a function for adding two integers. (The compiler I used was gcc 4.6.2 under MinGW.) In the example, the type for addInt_f has been explicitly defined using function<> while addInt_l is a lambda whose type is type-inferenced with auto.

When I compiled the code, the flip function can accept the explicitly type-defined version of addInt but not the lambda version, giving an error saying that, testCppBind.cpp:15:27: error: no matching function for call to 'flip(<lambda(int, int)>&)'

The next few lines show that the lambda version (as well as a 'raw' version) can be accepted if it's explicitly cast to the appropriate function<> type.

So my questions are:

  1. Why is it that a lambda function does not have a function<> type in the first place? In the small example, why does not addInt_l have function<int (int,int)> as the type instead of having a different, lambda type? From the perspective of functional programming, what's the difference between a function/functional object and a lambda?

  2. If there is a fundamental reason that these two have to be different. I heard that lambda's can be converted to function<> but they are different. Is this a design issue/defect of C++11, an implementation issue or is there a benefit in distinguishing the two as the way it is? It seems that the type-signature of addInt_l alone has provided enough information about the parameter and return types of the function.

  3. Is there a way to write the lambda so that the above mentioned explicit type-casting can be avoided?

Thanks in advance.

    //-- testCppBind.cpp --
    #include <functional>
    using namespace std;
    using namespace std::placeholders;

    template <typename T1,typename T2, typename T3>
    function<T3 (T2, T1)> flip(function<T3 (T1, T2)> f) { return bind(f,_2,_1);}

    function<int (int,int)> addInt_f = [](int a,int b) -> int { return a + b;};
    auto addInt_l = [](int a,int b) -> int { return a + b;};

    int addInt0(int a, int b) { return a+b;}

    int main() {
      auto ff = flip(addInt_f);   //ok
      auto ff1 = flip(addInt_l);  //not ok
      auto ff2 = flip((function<int (int,int)>)addInt_l); //ok
      auto ff3 = flip((function<int (int,int)>)addInt0);  //ok

      return 0;
    }

thor
  • 21,418
  • 31
  • 87
  • 173
  • 4
    You should not be taking `std::function` arguments, mainly because it inhibits type deduction (which is your problem here). – R. Martinho Fernandes Jul 24 '12 at 10:22
  • 1
    Related: [C++11 does not deduce type when std::function or lambda functions are involved](http://stackoverflow.com/q/9998402/487781) – hardmath Jul 24 '12 at 10:32
  • 1
    Lambdas are transformed to anonymous Functors (or Functions if they don't capture an environment). Transforming them to std::function would introduced a strong coupling between the language and the library and would therefore would be a very bad idea. – MFH Jul 24 '12 at 10:33
  • @MFH Lambdas are always anonymous function objects. Those function objects can then be converted to function pointers. – R. Martinho Fernandes Jul 24 '12 at 10:50
  • @MFH. I think both lambda's and function<> are library constructs introduced in C++11. So theoretically, lambda's could have been defined to return function<> object's. I don't see why that's impossible or how that interferes with the language. – thor Jul 25 '12 at 23:39
  • 3
    @TingL That's where you're wrong. Lamdas are NOT library constructs but a new language feature. – MFH Jul 26 '12 at 08:59

2 Answers2

41

std::function is a tool useful to store any kind of callable object regardless of its type. In order to do this it needs to employ some type erasure technique, and that involves some overhead.

Any callable can be implicitly converted to a std::function, and that's why it usually works seamlessly.

I'll repeat to make sure it becomes clear: std::function is not something just for lambdas or function pointers: it's for any kind of callable. That includes things like struct some_callable { void operator()() {} };, for example. That is a simple one, but it could be something like this instead:

struct some_polymorphic_callable {
    template <typename T>
    void operator()(T);
};

A lambda is just yet another callable object, similar to instances of the some_callable object above. It can be stored in a std::function because it's callable, but it doesn't have the type erasure overhead of std::function.

And the committee plans to make lambdas polymorphic in the future, i.e., lambdas that look like some_polymorphic_callable above. Which std::function type would such a lambda be?


Now... Template parameter deduction, or implicit conversions. Pick one. That's a rule of C++ templates.

To pass a lambda as a std::function argument, it needs to be implicitly converted. Taking a std::function argument means that you're choosing implicit conversions over type deduction. But your function template needs the signature to be deduced or provided explicitly.

The solution? Don't restrict your callers to std::function. Accept any kind of callable.

template <typename Fun>
auto flip(Fun&& f) -> decltype(std::bind(std::forward<Fun>(f),_2,_1))
{ return std::bind(std::forward<Fun>(f),_2,_1); }

You may now be thinking why do we need std::function then. std::function provides type erasure for callables with a known signature. That essentially makes it useful to store type-erased callables and to write virtual interfaces.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • :Thanks for your clear explanation. IMHO, the fact that std::function can only be used for storage is rather disappointing. If I understand your answer correctly, this is to avoid overhead? Then, does the same go for the smart pointers? Also, in your `some_polymorphic_callable` example, I don't see why function<> type cannot express the type for the templated lambda's since function<> can already have template params. If overhead is the only issue, has anyone considered a lightweight function<> can be used in parameter types? It would make STL much more usable. – thor Jul 26 '12 at 00:00
  • Also, in the `decltype` example, if the function body is 25 lines of code instead of a one-liner, do we need to include 25 lines in decltype? In addition, the use of `` looks almost like void* or a macro to me. Mis-usage of `Fun` can only be detected when a compile error occurs. By comparison, a function<> parameter type (if usable) would clearly tell the person and the computer the type signature. Admittedly, I have very limited knowledge about "type erasure" and the like. Thanks for telling me the rules. I am just wondering why it has to be this way. – thor Jul 26 '12 at 00:14
  • @TingL The type for the polymorphic lambda cannot be expressed because `function<>` takes *one* signature argument, while the polymorphic lambda would have *an unbounded number of different signatures* (that's why it's called polymorphic: it has many forms). The overhead issue doesn't quite apply the same for smart pointers. `unique_ptr` has truly *zero* overhead (it was one of the design goals). `shared_ptr` can have some overhead if you're not running a multithreaded program, but multithreaded programs are a case where it is more likely to be used. – R. Martinho Fernandes Jul 26 '12 at 00:47
  • The decltype in my example is not mandatory. I used it because it saves me the hassle of computing the return type myself. If the function has many lines, you can get away with just placing something like the expression that is return in `decltype` (it's all that matters for computing the return type after all). But sometimes it does get a bit hairy :( – R. Martinho Fernandes Jul 26 '12 at 00:47
  • I am quite sympathetic with your worries about catching errors. Sadly, the concepts proposal (which would have addressed that issue) was not really ready and was not included in this revision of the standard. Still, currently you can get nice errors with a `static_assert` and a `is_callable` trait (sadly not in the standard library, so you have [to write your own](https://bitbucket.org/martinhofernandes/wheels/src/default/include/wheels/type_traits.h++#cl-275)). – R. Martinho Fernandes Jul 26 '12 at 00:47
  • This is getting a bit long-winded, and it's sleepy time in my timezone :). If there's something that's still not clear you can come by the [chat](http://chat.stackoverflow.com/rooms/10/loungec) later, which is much better for this kind of thing. I'm usually there during European day time. – R. Martinho Fernandes Jul 26 '12 at 00:47
  • @Martinho: Thanks for your answer. I came across an old post the other day about C++ templates: http://www.kuro5hin.org/story/2003/5/26/22429/7674 and I am beginning to understand the root of some of these problems (e.g. why type-inferencing can't work with type conversion in C++ as you described). Maybe it's just too much to expect from C++ template something more type-safe or functional like you would find in OCaml or Haskell. Anyways, thx for the answer – thor Aug 02 '12 at 03:53
  • BTW, I tried to add type conversion operators to function<> (at the cost of the small overhead) because I can't tolerate the complexity of the syntax required for such simple things; and apparently, this does not seem to be possible. Any pointers on how to enable the automatic conversion (from lambda to function<>)? – thor Aug 02 '12 at 04:00
  • An automatic conversion from lambdas to function<> already exists. Pretty much everything is convertible to function<>. I don't really understand what you mean there :/ – R. Martinho Fernandes Aug 02 '12 at 10:20
5
  1. Because function<> employs type erasure. This allows several different function-like types to be stored in a function<>, but incurs a small runtime penalty. Type erasure hides the actual type (your specific lambda) behind a virtual function interface.
  2. There is a benefit to this: one of the C++ design "axioms" is to never add overhead unless it is really needed. Using this setup, you do not have any overhead when using type inference (use auto or pass as a template parameter), but you still have all the flexibility to interface with non-template code through function<>. Also note that function<> is not a language construct, but a component of the standard library that can be implemented using simple language features.
  3. No, but you can write the function to just take the type of the function (language construct) instead of the specifics of the function<> (library construct). Of course, that makes it a lot harder to actually write down the return type, since it does not directly give you the parameter types. However, using some meta-programming a la Boost.FunctionTypes you can deduce these from the function you pass in. There are some cases where this is not possible though, for example with functors that have a templated operator().
ltjax
  • 15,837
  • 3
  • 39
  • 62