std::function with templated arguments - c++

I want to write a templated function that applies some function over pairs of elements coming from two vectors. The result should be a new vector of results. I want this to be a templated function so that it works on different types.
I tried the definition before. However, when I try to apply it to some particular function, I get a compilation error.
#include <vector>
#include <cmath>
#include <iostream>
#include <functional>
using namespace std;
template<typename T1, typename T2, typename T3>
vector<T3> mapzip2(const vector<T1> &xs, const vector<T2> &ys, std::function<T3(T1, T2)> l) {
if (xs.size() != ys.size())
throw runtime_error("mapzip2: container sizes (" + to_string(xs.size()) +
" and " + to_string(ys.size()) + ") do not match");
vector<T3> result;
result.reserve(xs.size());
for (int i = 0; i < xs.size(); i++)
result.push_back(l(xs[i], ys[i]));
return result;
}
constexpr double PRECISION = 1E-6;
bool feq(double a, double b) {
return abs(a - b) < PRECISION;
}
int main() {
vector<double> a = {0.3, 0.42, 0.0, -7.34};
vector<double> b = {0.3, 0.42, 0.0, -7.34};
// compilation error: no matching function for call to
// ‘mapzip2(std::vector<double>&, std::vector<double>&, bool (&)(double, double))’
vector<bool> result = mapzip2(a, b, feq);
for (bool b: result) cout << b << ' ';
cout << endl;
}
What is wrong here with types deduction?

You have a sort of chicken-and-egg problem.
The T3 type in
template<typename T1, typename T2, typename T3>
T3 mapzip2(const vector<T1> &xs, const vector<T2> &ys, std::function<T3(T1, T2)> l)
has to be deduced from the third argument, a std::function<T3(T1, T2)>
But when you call
bool feq(double a, double b) {
return abs(a - b) < PRECISION;
}
// ...
vector<bool> result = mapzip2(a, b, feq);
you call mapzip() with feq that can be converted to a std::function<bool(double, double)> but isn't a std::function<bool(double, double)>
So the T3 type can't be deduced as bool because to convert feq to std::function<bool(double, double)> you have to know, before the deduction, that T3 is bool.
Possible solutions:
(1) explicit the template types calling mapzip()
vector<bool> result = mapzip2<double, double, bool>(a, b, feq);
This way the compiler know that T3 is bool, so convert feq to std::function<bool(double, double)>
(2) construct a std::function<bool(double, double)> with feq
vector<bool> result = mapzip2(a, b, std::function<double, double, bool>{feq});
so the compiler can receive a std::function as third argument and deduce T3 from it.
(3) (more flexible and, IMHO, the best of the three) avoid std::function and use a more generic functional typename for the function
template<typename T1, typename T2, typename F>
auto mapzip2(const vector<T1> &xs, const vector<T2> &ys, F l) {
if (xs.size() != ys.size())
throw runtime_error("mapzip2: container sizes (" + to_string(xs.size()) +
" and " + to_string(ys.size()) + ") do not match");
vector<decltype(l(xs[0], ys[0]))> result; // <-- use decltype() !
result.reserve(xs.size());
for (int i = 0; i < xs.size(); i++)
result.push_back(l(xs[i], ys[i]));
return result;
}
Observe the use of decltype() to deduce the type of the returned vector (the old T3) and the use of auto (starting from C++14) for the returned type of the function.
If you can't use C++14 (only C++11), you have to add the trailing return type
template<typename T1, typename T2, typename F>
auto mapzip2(const vector<T1> &xs, const vector<T2> &ys, F l)
-> std::vector<decltype(l(xs[0], ys[0]))>
{
}
Observe also -- as pointed by ypnos in a comment -- that the signature of your original mapzip2() is wrong: you return result, a std::vector<T3>, not a T3.

The problem is that template functions don't infer types and don't do implicit casting (if you don't supply the types, just let the compiler generate the function). The compiler just tries to find a simple match. Consider this example:
template<typename T>
T add2(T a, T b)
{
T res = a + b;
return res;
}
int main()
{
int a = add2(10, 20); // ok
double b = add2(10.2, 20); // error, no implicit cast from int to double
return 0;
}
The second assignment in main will emit no matching function for call to ‘add2(double, int)’ error.
As in your case, you pass feq which is of type bool (*)(double, double), that is, it's a function pointer, while mapzip2 expects std::function object. There is no implicit casting for templates.
As others suggested, you can build the function object explicitly.
(also as others noted, you need to return vector<T3>, not just T3, but this is a second problem, not related to the original one).
Finally, if you do supply the template types, the compiler will indeed try implicit casting, for example, in the above example, the following will work:
double b = add2<double>(10.2, 20);

The Standard Library solves this issue by using iterators. It would be a good idea to use them too since your code has the same structure as a standard algorithm:
// Overload #1
template<class I1, class I2, class O, class F>
void zip(I1 begin1, I1 end1, I2 begin2, O out_it, F f) {
while (begin1 != end1) {
out_it++ = f(*begin1++, *begin2++);
}
}
// Overload #2
template<class C1, class C2, class R, class F>
void zip(C1& c1, C2& c2, R& ret, F f) {
using std::begin; using std::end;
zip(begin(c1), end(c1), begin(c2), std::back_inserter(ret), f);
}
vector<bool> result;
zip(a, b, result, feq);
Or just use std::transform().
If you still want to return the vector from the function, it would help to decouple the return type deduction from the function itself:
template<class T> using value_t = typename std::decay_t<T>::value_type;
template<class F,class... Cs> using zip_ret = std::result_of_t<F&(value_t<Cs>...)>;
template<class C1, class C2, class F, class R=zip_ret<F, C1, C2>>
std::vector<R> zip(C1& c1, C2& c2, F f) {
using std::begin; using std::end;
std::vector<R> ret;
std::transform(begin(c1), end(c1), begin(c2), std::back_inserter(ret), f);
return ret;
}

Related

How does template function work with multiple typenames which may share an intersection?

I am new to C++ and currently trying to understand how template functions work. First I wanted to add two numerical values of the same type, which is quite easy to understand.
template <typename T>
T add(T a, T b){return a+b;}
int main(){
float a_f=2.5;float b_f=1.5;float c_f;
int a_i=2;int b_i=1;int c_i;
c_f = add(a_f, b_f);
c_i = add(a_i, b_i);
return 0;
}
Next I wanted to add two numerical numbers with different and equal types. My naive assumption was this:
template<typename R, typename S, typename T>
R add(S a, T b){return a+b;}
int main(){
float a=3.2; int b=2;
auto result1 = add(a,b); // error: no matching function for call to ‘add(float&, int&)’
auto result2 = add(a,a); // error: no matching function for call to ‘add(float&, float&)’
auto result3 = add(b,b); // error: no matching function for call to ‘add(int&, int&)’
return 0;
}
I am aware that this approach is not correct, because the typename's share an intersection regarding data types and therefore the declaration itself cannot be correct.
How could a simple add() function be implemented that adds two numeric values together, regardless of type?
The problem is not with an intersection, but that it cant deduce R. In
template<typename R, typename S, typename T>
R add(S a, T b){return a+b;}
There is nothing telling the compiler what R should be. It's not deduced from the variable you are assigning the result to and you do not specify it, so there is no valid call that it can do. To fix this you can just get rid of R and use the auto return type to have it deduced for you like
template<typename S, typename T>
auto add(S a, T b){return a+b;}
How could a simple add() function be implemented that adds two numeric values together, regardless of type?
In C++14:
template<class T, class U>
auto add(T t, U u) {
return t + u;
}
In the above, the type of the return value is deduced from the type of expression t + u.
C++11 doesn't deduce return types but allows for trailing return type, so a C++11 version is:
template<class T, class U>
auto add(T t, U u) -> decltype(t + u) {
return t + u;
}
What you want is to deduce the return type. However, type deduction for type template parameter only apply for function parameter.
Even though typenames are not the right tool to do that, C++ offers other mean to deduce the type.
Use auto
You can simply put auto there and let the compiler deduce from the return type:
template<typename S, typename T>
auto add(S a, T b){ return a + b; }
Use trailing return types
You can also use the trailing return type if you want to be more explicit on the return type of the function:
template<typename S, typename T>
auto add(S a, T b) -> decltype(a + b) { return a + b; }

How to take a class, that uses a parameter pack and typename, as an input parameter for a function(c++)

I created a class that takes a typename template variable and a parameter pack. In the next step i want to be able to pass two objects of that class to my function.
My main problem is, passing the template parameters and the objects correctly to be able to use my function.
My class implementation.
//auto as template parameter is for non-type parameter(c++17)
template <auto value> constexpr auto DIM = value;
//constexpr on values in header files(c++17)
inline constexpr auto const DIM3 = DIM <3>;
inline constexpr auto const DIM2 = DIM <2>;
enum Index : int {lower = 0, upper = 1};
template<int base, int exponent>
int constexpr pow(){
if constexpr(exponent == 0){
return 1;
}else{
return base * pow<base, exponent-1>();
}
}
template<int Size, typename T>
struct Array{
T array[Size];
Array(const T * a){
for(int i = 0; i < Size; i++){
array[i] = a[i];
}
}
};
//auto as template parameter is for non-type parameters(c++17)
template<typename T = double, auto ...IndicesN>
class MatrixND{
private:
const Array<pow<DIM3, sizeof...(IndicesN)>(), T> matrix;
public:
MatrixND(const T * arr): matrix(arr){}
template<auto ...args>
auto constexpr getElement(){
}
};
The function that takes MatrixND objects:
template<auto posT1, auto posT2, typename A, typename B>
auto constexpr function(const MatrixND<A> tensor1, const MatrixND<B> tensor2){
return 0;
}
I tried the following, but it throws an error message "no matching function call":
const double arrayc[27] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27};
auto matrix1 = new MatrixND<double, upper, lower, lower>(arrayc);
function<1,1, decltype(matrix1), decltype(matrix1)>(matrix1, matrix1);
The error message:
error: no matching function for call to ‘function<1, 1, MatrixND<double, (Index)1, (Index)0, (Index)0>*, MatrixND<double, (Index)1, (Index)0, (Index)0>*>(MatrixND<double, (Index)1, (Index)0, (Index)0>*&, MatrixND<double, (Index)1, (Index)0, (Index)0>*&)’
contraction<1,1, decltype(matrix1), decltype(matrix1)>(matrix1, matrix1);
You need to add the parameter packs to the template parameters of function. Using
template<auto posT1, auto posT2, typename A, typename B, auto ...AIndicesN, auto ...BIndicesN>
auto constexpr function(const MatrixND<A, AIndicesN...> tensor1, const MatrixND<B, BIndicesN...> tensor2){
return 0;
}
Allows you to call function like
auto foo = function<1,1>(matrix1, matrix1);
Do note that for this to compile with your code you need to change
auto matrix1 = new MatrixND<double, upper, lower, lower>(arrayc);
to
auto matrix1 = MatrixND<double, upper, lower, lower>(arrayc);
since you don't actually want a pointer to a MatrixND but an actual MatrixND object.
When you call the function as
function<1,1, decltype(matrix1), decltype(matrix1)>(matrix1, matrix1);
you say that the template arguments A and B are decltype(matrix1), meaning they are really MatrixND<double, upper, lower, lower>.
That in turn means that e.g. the argument tensor1 will have the type const MatrixND<MatrixND<double, upper, lower, lower>>. Which is not what you pass.
A possible solution would be to not use MatrixND<A> (and MatrixND<B>) in the argument list, but only
template<auto posT1, auto posT2, typename A, typename B>
auto constexpr function(const A tensor1, const B tensor2){
return 0;
}
And you should probably pass references instead of values as arguments as well.
If you do like above with the function you also don't need the template arguments for the types of A and B as that would be deduced by the compiler:
function<1,1>(matrix1, matrix1);

How to create a generic function for combining binary predicates?

There are several related questions, e.g. algorithms with multiple (unary) predicates, or multiple predicates in general. But they do not seem to tackle the generic case that I'm aiming at, and the answers are often out-dated in that they refer to constructs that are deprecated in C++11. It might all boil down to this question related to type deduction in lambdas, but I'm not sure whether it can be considered as a duplicate.
I tried to create a function that combines two arbitrary binary predicates (in the meaning of "types that implement a two-argument operator() and return a bool"). My goal was to have the possibility to write code like this:
auto p0 = somePredicate();
auto p1 = someOtherPredicate();
auto p2 = evenMorePredicates();
auto combined = and(p0, or(p1, p2));
I know that something similar can be achieved with lambdas (as also suggested in the answer to one of the questions linked above), but this requires the argument types to be repeated in the lambda itself. I'd like to know how such an and or or function could really be implemented generically - particularly, for binary predicates with arbitrary argument types.
My basic approach (and a suggestive example) is the following:
#include <functional>
#include <iostream>
template <typename A, typename B, typename P0, typename P1>
std::function<bool(const A&, const B&)> and(
const P0& p0, const P1& p1)
{
return [p0, p1](const A& a, const B& b)
{
return p0(a, b) && p1(a, b);
};
}
int main(int argc, char* argv[])
{
auto p0 = [](const int& a, const int& b)
{
return a < 0 && b > 0;
};
auto p1 = [](const int& a, const int& b)
{
return a < 1 && b > 4;
};
// This is what I want:
//auto combined = and(p0, p1);
// This works, but ...
auto combined = and<int, int, std::function<bool(const int&, const int&)>, std::function<bool(const int&, const int&)>>(p0, p1);
std::cout << "combined(-3,7) : " << combined(-3, 7) << std::endl;
std::cout << "combined(-3,1) : " << combined(-3, 1) << std::endl;
return 0;
}
In any case, the problem seems to be that the template parameters of the argument types can not be deduced at the call site. I tried variations thereof, based on other related questions here on stackoverflow, but no avail. My gut feeling is that there must be a (simple) solution for this, sneaking around some limitation of the type deduction system of lambdas, but I obviously didn't find the right path yet.
You could write a functor class to carry your expression:
template <typename P0, typename P1>
class AndFunctor {
public:
AndFunctor (P0 p0, P1 p1)
: m_p0{std::move(p0)}, m_p1{p1}
{}
template <typename T, typename U>
bool operator() (T&& t, U&& u) {
return m_p0(t, u) && m_p1(t, u);
}
private:
P0 m_p0;
P1 m_p1;
};
Then return an instance of this in your and function (and is a keyword, so this code renames it to and_p
template <typename P0, typename P1>
AndFunctor<P0,P1> and_p(P0 p0, P1 p1)
{
return { std::move(p0), std::move(p1) };
}
Then you just use it as you envisaged:
auto combined = and_p(p0, p1);
Live Demo

the following c++ program does not compile and is displaying a variety of errors

So I was reading about auto and other C++11 std feature and made this program:
#include<iostream>
#include<tuple>
using namespace std;
template<typename T1, typename T2>
auto create_pair(const T1 &a, const T2 &b)
{
pair<T1, T2> p(a, b);
return p;
}
int main()
{
auto p1 = create_pair(4, 5.6);
pair<int, float> p(p1);
cout << get<0>(p);
cout << endl;
cout << get<1>(p);
return 0;
}
My question is whether auto can be used in the way prescribed here in the create_pair() function or there exists some other workaround this to achieve the same result.
The error message when compiled with gcc is this:
create_pair function uses auto type specifier without return type.
Automatic return type deduction is a C++14 feature, and your code would work if compiled with C++14. The auto keyword in the return type location is part of the C++11 trailing return type feature, where you specify the return type right before the body with an arrow and the type (e.g., auto foo() -> int {...}).
Without macros, the closest you can get in C++11 is a lambda function with a single statement (the return statement) as its body, and this won't work with templates, and can cause slight issues related to the function being a variable instead of a function.
With macros, you can have something to the effect of auto foo() RETURNS(2), which would expand to auto foo() -> decltype(2) {return 2;}
On a side note, this function exists already; it is std::make_pair, which is implemented a bit more usefully than your version.
The error message fully describes what is going on. In C++11, auto in place of function return type is not return type deduction, but only an alternative way to actually specify the return type, and should be used like this:
template<typename T1, typename T2>
auto create_pair(const T1 &a, const T2 &b) -> std::pair<T1, T2>;
It is intended to be used in contexts like
template <typename T1, typename T2>
auto add (T1 const & x1, T1 const & x2) -> decltype(x1 + x2)
{
return x1 + x2;
}
instead of much uglier
template <typename T1, typename T2>
decltype(std::declval<T1>() + std::declval<T2>()) add (T1 const & x1, T1 const & x2)
{
return x1 + x2;
}
In C++11 return type deduction is available only for lambdas:
auto create_pair = [](int x, int y){ return std::pair<int, int>(x, y); }
Since your function is a template (and generic lambdas allowing to be templated are also C++14 feature), this would not help much.
However, your code should compile under C++14, as the latter brings return type deduction with exactly the same syntax you are using in your code.

c++11: How to write a wrapper function to make `std::function` objects

I am trying to write a wrapper make_function, which like std::make_pair can create a std::function object out of suitable callable objects.
Just like make_pair, for a function pointer foo, auto f0 = make_function(foo); creates a std::function function object f0 of the right type signature.
Just to clarify, I don't mind occasionally giving type parameters to make_function in case it is difficult (or impossible) to deduce the type entirely from the parameters.
What I came up with so far (code below) works fine for lambdas, some function pointers, and functors (I didn't consider volatiles). But I couldn't get it work for std::bind or std::bind<R> results. In the code below
auto f2 = make_function(std::bind(foo,_1,_2,_3)); //not OK
wouldn't compile/work, with gcc 4.8.1. I am guessing that I didn't capture the operator() for the bind result correctly, but I am not sure how to fix it.
Any help on how to fix this case or improvement in other corner cases is appreciated.
My question is, of course, how to fix the error in the example below.
For background, one of the cases I use this wrapper can be found at this question: How to make C++11 functions taking function<> parameters accept lambdas automatically. If you do not approve the use of std::function or my specific way of using it, please leave your comments in that post, and discuss technical issues here.
--- EDIT ---
From some of the comments, I learned that it's because of the ambiguity issue (ambiguity of the function call operator() of std::bind results). As pointed out by #Mooing Duck's answer, the solution is to give the parameter types explicitly. I have updated the code to combine the three functions in #Mooing Duck's answer (with slight change of type parameters), so that the make_function wrapper can now handle/type-deduce unambiguous cases as before, and allow specification of complete type signature when there is ambiguity.
(My original code for the unambiguous cases is at: https://stackoverflow.com/a/21665705/683218 and can be tested at: https://ideone.com/UhAk91):
#include <functional>
#include <utility>
#include <iostream>
#include <functional>
using namespace std;
// For generic types that are functors, delegate to its 'operator()'
template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) > {
enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
// for function pointers
template <typename ReturnType, typename... Args>
struct function_traits<ReturnType (*)(Args...)> {
enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
template <typename L>
static typename function_traits<L>::f_type make_function(L l){
return (typename function_traits<L>::f_type)(l);
}
//handles bind & multiple function call operator()'s
template<typename ReturnType, typename... Args, class T>
auto make_function(T&& t)
-> std::function<decltype(ReturnType(t(std::declval<Args>()...)))(Args...)>
{return {std::forward<T>(t)};}
//handles explicit overloads
template<typename ReturnType, typename... Args>
auto make_function(ReturnType(*p)(Args...))
-> std::function<ReturnType(Args...)> {
return {p};
}
//handles explicit overloads
template<typename ReturnType, typename... Args, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...))
-> std::function<ReturnType(Args...)> {
return {p};
}
// testing
using namespace std::placeholders;
int foo(int x, int y, int z) { return x + y + z;}
int foo1(int x, int y, int z) { return x + y + z;}
float foo1(int x, int y, float z) { return x + y + z;}
int main () {
//unambuiguous
auto f0 = make_function(foo);
auto f1 = make_function([](int x, int y, int z) { return x + y + z;});
cout << make_function([](int x, int y, int z) { return x + y + z;})(1,2,3) << endl;
int first = 4;
auto lambda_state = [=](int y, int z) { return first + y + z;}; //lambda with states
cout << make_function(lambda_state)(1,2) << endl;
//ambuiguous cases
auto f2 = make_function<int,int,int,int>(std::bind(foo,_1,_2,_3)); //bind results has multiple operator() overloads
cout << f2(1,2,3) << endl;
auto f3 = make_function<int,int,int,int>(foo1); //overload1
auto f4 = make_function<float,int,int,float>(foo1); //overload2
return 0;
}
Ideone
The problem is your code doesn't handle lambdas, bind, or functionoids properly, your code assumes that all of these take no parameters. To handle these, you'll have to specify the parameter types:
//plain function pointers
template<typename... Args, typename ReturnType>
auto make_function(ReturnType(*p)(Args...))
-> std::function<ReturnType(Args...)>
{return {p};}
//nonconst member function pointers
template<typename... Args, typename ReturnType, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...))
-> std::function<ReturnType(Args...)>
{return {p};}
//const member function pointers
template<typename... Args, typename ReturnType, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...) const)
-> std::function<ReturnType(Args...)>
{return {p};}
//qualified functionoids
template<typename FirstArg, typename... Args, class T>
auto make_function(T&& t)
-> std::function<decltype(t(std::declval<FirstArg>(), std::declval<Args>()...))(FirstArg, Args...)>
{return {std::forward<T>(t)};}
//unqualified functionoids try to deduce the signature of `T::operator()` and use that.
template<class T>
auto make_function(T&& t)
-> decltype(make_function(&std::remove_reference<T>::type::operator()))
{return {std::forward<T>(t)};}
Variables:
int func(int x, int y, int z) { return x + y + z;}
int overloaded(char x, int y, int z) { return x + y + z;}
int overloaded(int x, int y, int z) { return x + y + z;}
struct functionoid {
int operator()(int x, int y, int z) { return x + y + z;}
};
struct functionoid_overload {
int operator()(int x, int y, int z) { return x + y + z;}
int operator()(char x, int y, int z) { return x + y + z;}
};
int first = 0;
auto lambda = [](int x, int y, int z) { return x + y + z;};
auto lambda_state = [=](int x, int y, int z) { return x + y + z + first;};
auto bound = std::bind(func,_1,_2,_3);
Tests:
std::function<int(int,int,int)> f0 = make_function(func); assert(f0(1,2,3)==6);
std::function<int(char,int,int)> f1 = make_function<char,int,int>(overloaded); assert(f1(1,2,3)==6);
std::function<int(int,int,int)> f2 = make_function<int,int,int>(overloaded); assert(f2(1,2,3)==6);
std::function<int(int,int,int)> f3 = make_function(lambda); assert(f3(1,2,3)==6);
std::function<int(int,int,int)> f4 = make_function(lambda_state); assert(f4(1,2,3)==6);
std::function<int(int,int,int)> f5 = make_function<int,int,int>(bound); assert(f5(1,2,3)==6);
std::function<int(int,int,int)> f6 = make_function(functionoid{}); assert(f6(1,2,3)==6);
std::function<int(int,int,int)> f7 = make_function<int,int,int>(functionoid_overload{}); assert(f7(1,2,3)==6);
std::function<int(char,int,int)> f8 = make_function<char,int,int>(functionoid_overload{}); assert(f8(1,2,3)==6);
http://coliru.stacked-crooked.com/a/a9e0ad2a2da0bf1f The only reason your lambda was succeeding is because it was implicitly convertible to a function pointer because your example doesn't capture any state. Note that my code requires the parameter types for overloaded functions, functionoids with overloaded operator() (including bind), but is now able to deduce all non-overloaded functionoids.
The decltype lines are complicated but they're used to deduce the return types. Notice that in NONE of my tests do I need to specify the return type. Let's break down make_function<short,int,int> down as if T is char(*)(short, int, int):
-> decltype(t(std::declval<FirstArg>(), std::declval<Args>()...))(FirstArg, Args...)
`std::declval<FirstArg>()` is `short{}` (roughly)
-> decltype(t(short{}, std::declval<Args>()...))(FirstArg, Args...)
`std::declval<Args>()...` are `int{}, int{}` (roughly)
-> decltype(t(short{}, int{}, int{})(FirstArg, Args...)
`t(short{}, int{}, int{})` is an `int{}` (roughly)
-> decltype(short{})(FirstArg, Args...)
`decltype(int{})` is `int`
-> int(FirstArg, Args...)
`FirstArg` is still `short`
-> int(short, Args...)
`Args...` are `int, int`
-> int(short, int, int)
So this complex expression merely figures out the function's signature
well, that should look familiar...
In general you cannot solve it without the severe restriction that whatever you pass to make_function is only callable with exactly one signature.
What are you going to do with something like:
struct Generic
{
void operator()() { /* ... */ }
void operator()() const { /* ... */ }
template<typename T, typename... Ts>
T operator()(T&& t, Ts&&...) { /* ... */ }
template<typename T, typename... Ts>
T operator()(T&& t, Ts&&...) const { /* ... */ }
};
C++14 generic lambdas will have the same issue.
The signature in std::function is based on how you plan to call it and not on how you construct/assign it.
You cannot solve it for std::bind either, as that has indefinite arity:
void foo() { std::cout << "foo()" << std::endl; }
//...
auto f = std::bind(foo);
f(); // writes "foo()"
f(1); // writes "foo()"
f(1, 2, 3, 4, 5, 6); // writes "foo()"
The big reason why you want to be able to convert lambdas to std::function is because you want two overloads, each taking different signatures.
A good way to solve this involves std::result_of.
Suppose you are making a loop control structure that takes a lambda or other functional. If that functional returns void, you want to loop uncontrolled. If it returns bool or the like, you want to loop while it returns true. If it returns enum ControlFlow, you want to pay attention to the ControlFlow return value (continue or break, say). The function in question takes either the element iterating over, and optionally some extra data (the index in the iteration, maybe some "location" meta-information about the element, etc).
std::result_of would let you pretend to invoke the passed in type with a different number of arguments. A traits class could then figure out which of the above signatures is the "best match", and then route to the implementation that handles that signature (possibly by wrapping the "simpler" cases in a lambda and calling the more complex cases).
Naively, your make_function would could this problem, because you could then simply overload on the various std::function< blah(etc) > cases. But with auto parameters coming down the pipe, and std::bind already doing perfect forwarding, this only handles the easiest cases.
std::result_of traits classes (and possibly related concept matching and requires clauses) and tag dispatching (or SFINAE as a last resort).
The big downside is that you end up having to manage the override order yourself semi-manually. I could see some utility in helper classes where you provide a list of signatures to match, and it either produces a boost::variant or you also produce a canonical output and a conversion method to that canonical output.
The short answer? std::bind's implementation is implementation specific details, but it may involve the equivalent of perfect forwarding of variadic parameter packs, and as such is not suitable for your "get the address of the one and only concrete operator()" technique you are using.
As another example:
template <typename A,typename B>
vector<B> map(std::function<B (A)> f, vector<A> arr) {
vector<B> res;
for (int i=0;i<arr.size();i++) res.push_back(f(arr[i]));
return res;
}
should be written as:
template<typename expression>
using result = typename std::result_of<expression>::type;
template<typename expression>
using decayed_result = typename std::decay<result<expression>>::type;
template <typename function,typename B>
vector<decayed_result<function(B)>> map(function&& f, vector<A> const& arr) {
vector<decayed_result<function(B)>> res;
res.reserve( arr.size() );
for (A const& a : arr) {
res.push_back( f(a) );
}
return res;
}
again, result_of is the right solution, not converting things needlessly to std::function.
For fold_right we get:
template<bool b, typename T=void>
using EnableIf = typename std::enable_if<b,T>::type;
template<typename function, typename src, typename dest>
EnableIf<
std::is_convertible< result<function(src, dest)>, dest >::value,
std::vector<dest>
>
fold_right( function&& f, std::vector<src> const& v, dest initial )
which again skips any type erasure on f. And if you really want to do type erasure on f, you can do:
template<typename T> struct identity { typedef T type; };
template<typename T> using do_not_deduce = typename identity<T>::type;
template<typename src, typename dest>
std::vector<dest> fold_right( do_not_deduce< std::function<dest(src,dest)> > f, std::vector<src> const& v, dest init );
std::function is a type erasure object. You type erase because you want to use a type somewhere you do not want to carry the type over to. Deducing from a type what kind of resulting type erasure object you should create is almost always the wrong answer, because you have all of the dependency of non-type erased cases, and all of the inefficiency of the type erasure.
make_function's result is dependent the full type of the source, which makes type erasure output almost completely useless.