1

(C++11 newbie here) Trying some composition ideas with C++11 lambdas:

#include <iostream>
#include <functional>

struct A { };
struct B { };
struct C { };

B b4a (A a) {   B b; return b;   }
C c4b (B b) {   C c; return c;   }

I now print out the types as follows (the definition of type_name is a bit off-topic, but I have source of everything in this question building and working online here):

cout << "b4a : " << type_name<decltype(b4a)>() << endl;
cout << "c4b : " << type_name<decltype(c4b)>() << endl;
auto c4a = [=] (A a) { return c4b (b4a (a)); };
cout << "c4a : " << type_name<decltype(c4a)>() << endl;

which produces the following reasonable-looking output:

b4a : B (A)
c4b : C (B)
c4a : main::{lambda(A)#1}

I now try to abstract composition itself as follows:

template <typename R, typename S, typename T>
std::function<R (T)> compose 
(   std::function <R (S)> r4s
,   std::function <S (T)> s4t   ) 
{   return [=] (T t) {   r4s (s4t (t));  };  }

and I get the following errors

main.cpp: In function 'int main()':
main.cpp:44:33: error: no matching function for call to 'compose(C (&)(B), B (&)(A))'
     auto c4a = compose (c4b, b4a);
                                 ^
main.cpp:44:33: note: candidate is:
main.cpp:34:17: note: template<class R, class S, class T> std::function<R(T)> compose(std::function<R(S)>, std::function<S(T)>)
 function<R (T)> compose 
                 ^
main.cpp:34:17: note:   template argument deduction/substitution failed:
main.cpp:44:33: note:   mismatched types 'std::function<R(S)>' and 'C (*)(B)'
     auto c4a = compose (c4b, b4a);

which hint that there is a 'pointer-to-function-type' problem; but I thought std::function<...> was supposed to abstract that away?

Putting in explicit type arguments

auto c4a = compose<C, B, A> (c4b, b4a);

changes the errors, but does not help:

main.cpp: In instantiation of 'std::function<R(T)> compose(std::function<R(S)>, std::function<S(T)>) [with R = C; S = B; T = A]':

main.cpp:44:42:   required from here
main.cpp:37:42: error: could not convert '<lambda closure object>compose(std::function<R(S)>, std::function<S(T)>) [with R = C; S = B; T = A]::<lambda(A)>{std::function<C(B)>((*(const std::function<C(B)>*)(& r4s))), std::function<B(A)>((*(const std::function<B(A)>*)(& s4t)))}' from 'compose(std::function<R(S)>, std::function<S(T)>) [with R = C; S = B; T = A]::<lambda(A)>' to 'std::function<C(A)>'
 {   return [=] (T t) {   r4s (s4t (t));  };  }

Putting in explicit casts

auto c4a = compose (std::function<C(B)>(c4b), std::function<B(A)>(b4a));

or

auto c4a = compose<C, B, A> (std::function<C(B)>(c4b), std::function<B(A)>(b4a));

produces the same errors as above. Clues?

Praetorian
  • 106,671
  • 19
  • 240
  • 328
Reb.Cabin
  • 5,426
  • 3
  • 35
  • 64

1 Answers1

1

c++11 std::function does not accept lambda's of the "same type" implicitly, when template type deduction is also needed. In such a case, you have to explicitly either convert your lambda to a std::function object or store it in a std::function object before using it in your functional composition. It seems that current c++ standard cannot handle implicit type conversion and deduction of template types at the same time, probably because there are too many combinations of possibilities and also the associated ambiguity issue.

Please see my related question here: why do lambda functions in c++11 not have function<> types? if you are interested.

Basically, each lambda is just like a object with its own call operator (). So even if you have two lambda's both taking a A and returning a B, they are of two distinct types.

I have a wrapper make_function to do the type conversion for unambiguous cases here:

The following might work:

auto c4a = compose<C, B, A> (make_function(c4b), make_function(b4a));
Community
  • 1
  • 1
thor
  • 21,418
  • 31
  • 87
  • 173
  • 1
    Strictly speaking I don't think that's correct. As far as I can tell the statement `[](int i, int j) { return i + j; }` produces a lambda that is implicitly convertible to `std::function`. I think the issue comes when we attempt to implicitly convert a lambda to an std::function with unbound template parameters. – Chris Hayden Jun 19 '14 at 00:33
  • I'm intrigued, now, about how to declare `compose` without the `function` conversions (which I did get working). I should probably open up another question with those (failing) attempts. – Reb.Cabin Jun 19 '14 at 00:59
  • The smallest I've been able to get this is the following `template function compose ( R(r4s)(S) , S(s4t)(T) ) { return [=] (T t) { return r4s (s4t (t)); }; }` – Reb.Cabin Jun 19 '14 at 01:14
  • @ChrisHayden Thanks for the clarification. I've qualified the statement, and said implicit conversion is only an issue when there is also template type deduction going on at the same time. I think this is what caused the problem here. – thor Jun 19 '14 at 15:06