From these SE questions, Passing lambda as function pointer, Lambda as function parameter, Cannot pass lambda function as function reference?, I'm given to understand that I may pass stateless, non-capturing lambdas to functions that expect function pointers. Accordingly, I try a template to lift a binary function of type T (*bf)(T, T)
over std::vectors
:
template<typename T>
vector <T> lift_binary (const vector<T> & v1, const vector<T> & v2,
T (* bf)(T, T))
{ auto result = vector<T> ();
result.resize(v1.size());
transform (v1.begin(), v1.end(), v2.begin(), result.begin(), bf);
return result; }
This works when bf
is a named function, for example
template<typename T> T minus (T x, T y) { return x - y; }
template<typename T>
vector<T> operator- (const vector<T> &v1, const vector<T> &v2)
{ return lift_binary (v1, v2, minus); }
int main()
{ auto v1 = vector<double> ({1, 2, 3});
auto v2 = vector<double> ({10, 20, 30});
auto v3 = v1 - v2;
cout << v3[0] << " " << v3[1] << " " < v3[2] << " " << endl;
return 0; }
produces
-9 -18 -27
But it's no good with any of the three following lambda functions (the second one is uncommented, for instance):
template<typename T>
vector<T> operator- (const vector<T> &v1, const vector<T> &v2)
// { return lift_binary (v1, v2, [] (T x, T y) -> T { return x - y; } ); }
{ return lift_binary (v1, v2, [] (T x, T y) { return x - y; } ); }
// { return lift_binary (v1, v2, [] (auto x, auto y) { return x - y; } ); }
// { return lift_binary (v1, v2, minus); }
When compiled like this
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
The compiler can't match the parameter type T (*bf)(T, T)
against the types of the lambdas:
main.cpp: In instantiation of 'std::vector<_RealType> operator-(const std::vector<_RealType>&, const std::vector<_RealType>&) [with T = double]':
main.cpp:37:20: required from here
main.cpp:26:31: error: no matching function for call to 'lift_binary(const std::vector<double>&, const std::vector<double>&, operator-(const std::vector<_RealType>&, const std::vector<_RealType>&) [with T = double]::<lambda(double, double)>)'
{ return lift_binary (v1, v2, [] (T x, T y) { return x - y; } ); }
^
main.cpp:13:16: note: candidate: template<class T> std::vector<_RealType> lift_binary(const std::vector<_RealType>&, const std::vector<_RealType>&, T (*)(T, T))
vector <T> lift_binary (const vector<T> & v1, const vector<T> & v2, T (* bf)(T, T))
^
main.cpp:13:16: note: template argument deduction/substitution failed:
main.cpp:26:31: note: mismatched types 'T (*)(T, T)' and 'operator-(const std::vector<_RealType>&, const std::vector<_RealType>&) [with T = double]::<lambda(double, double)>'
{ return lift_binary (v1, v2, [] (T x, T y) { return x - y; } ); }
I get comparable errors with the other two lambda expressions. This leads me to think that no kind of generic lambda will match the parameter's function pointer type, but that doesn't jibe with what I have been able to find and read. I must be doing something else wrong.
Here is a coliru project with this sample online:
http://coliru.stacked-crooked.com/a/77756b84eb401156
and here is the entire code snippet in a block:
#include<iostream>
#include<vector>
#include<algorithm>
using std::cout;
using std::endl;
using std::vector;
template<typename T>
vector <T> lift_binary (const vector<T> & v1, const vector<T> & v2, T (* bf)(T, T))
{ auto result = vector<T> ();
result.resize(v1.size());
transform (v1.begin(), v1.end(), v2.begin(), result.begin(), bf);
return result; }
template<typename T>
T minus (T x, T y)
{ return x - y; }
template<typename T>
vector<T> operator- (const vector<T> &v1, const vector<T> &v2)
// { return lift_binary (v1, v2, [] (T x, T y) -> T { return x - y; } ); }
{ return lift_binary (v1, v2, [] (T x, T y) { return x - y; } ); }
// { return lift_binary (v1, v2, [] (auto x, auto y) { return x - y; } ); }
// { return lift_binary (v1, v2, minus); }
template<typename T>
vector<T> operator+(const vector<T> & v1, const vector<T> & v2)
{ return lift_binary (v1, v2, [] (T x, T y) -> T { return x + y; } ); }
int main()
{ auto v1 = vector<double> ({1, 2, 3});
auto v2 = vector<double> ({10, 20, 30});
auto v3 = v1 - v2;
cout << v3[0] << " "
<< v3[1] << " "
<< v3[2] << " " << endl;
return 0; }
EDIT:
The following works:
template<typename T>
vector<T> operator- (const vector<T> &v1, const vector<T> &v2)
{ auto result = vector<T> ();
result.resize(v1.size());
transform (v1.begin(), v1.end(), v2.begin(), result.begin(),
[] (T x, T y) { return x - y; } );
return result; }
and the whole motivation for the original question is just to allow me to abstract over this pattern so that I DRY (don't repeat your(my)self) when implementing other binary operators like +
, *
, etc.
I expected to be able to abstract out the lambda expression by writing a higher-order function lift_binary
that takes a T(*)(T,T)
as a parameter because I thought that's the type of the lambda.