How to overload a function that receives a lambda?

Hello! I'm sorry, I'm very new to C++, so the question can be very silly. So, sorry about it, but I failed to answer on my own. So!

I have a function that receives a lambda:

1
2
3
4
template <typename Λ>
auto call(Λ λ) -> decltype(λ()){
    return λ();
}


What I want is two have 2 different functions: for lambdas that return a value and for lambdas - that do not. So another overload would look like (pseudocode):

1
2
3
4
template <typename Λ>
auto call(Λ λ) -> decltype(λ()) == void{
    λ();
}


Okay, my the next though - is to use SFINAE to achieve that. Honestly, I've never used SFINAE before, so I failed:

1
2
3
4
5
6
7
8
9
10
11
template <typename Λ>
auto call(Λ λ) -> decltype(λ()) {
    static_assert(! std::is_same<decltype(λ()), void>(), "This overload can return a value of any type");
    return λ();
}

template <typename Λ>
void call(Λ λ) {
    static_assert(  std::is_same<decltype(λ()), void>(),  "This overload returns void");
    λ();
}

It even compiles! But, let's test it:

1
2
cout << call([] { return 1; }) << endl;
call([] { 2 * 2; });


These two `call` calls are ambiguous (at least, in MS VS). I  can't understand why - I'm using static_assert, so the call should not be ambiguous. If you can, please, explain me the situation.

Then, I tried another way:

1
2
3
4
5
6
7
8
9
10
template <typename Λ>
auto call(Λ λ) -> decltype(λ()){
    using namespace std;

    if (!is_same<decltype(λ()), void>()) {
        return λ();
    } else {
        cout << "Just do some work!" << endl;
    }
}


Suddenly, it works. I understand, that, roughly, it compiles to:

1
2
3
4
5
6
7
void call(Λ λ) {
    if (false) {
        return λ();
    } else {
        cout << "Just do some work!" << endl;
    }
}


and, for example:

1
2
3
4
5
6
7
int call(Λ λ) {
    if (true) {
        return λ();
    } else {
        cout << "Just do some work!" << endl;
    }
}


So, everything is correct, though, for some reasons, I don't like this approach. It looks more like a workaround, not like a real solution. But what would you do? Also, I wonder, what should I do, if I need to add overloads, that depend on lambda-parameters. For instance, it works:

1
2
3
void call(function<void(int, int) f){ ... }
void call(function<void(int) f){ ... }
void call(function<void(void) f){ ... }


But I don't want to have run-time penalty, because of using std::function! :)
https://en.cppreference.com/book/intro/function_overloading
Functions are identified not only by their names, but also by their arguments.

Return type of a function is neither name nor argument.

1
2
3
4
5
6
7
bool foo() { return false; } // error
void foo() {} // error

int main()
{
  foo();
}

 In function 'void foo()':
2:10: error: ambiguating new declaration of 'void foo()'
1:6: note: old declaration 'bool foo()' 
Though, it works! :)

1
2
3
4
5
6
7
int call(function<int(int, int)> f) { return f(1, 2); }
int call(function<int(int)> f) { return f(1); }
string call(function<string(void)> f) { return f();  }

cout << call([](int x, int y) { return x + y; }) << endl;
cout << call([](int x) { return x; }) << endl;
cout << call([]{ return "It workd!"; }) << endl;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <cstring>
#include <functional>

template < typename FN, typename... ARGS >
auto call( FN fn, ARGS&&... args )
{ return fn( std::forward<ARGS>(args)... ) ; }

int main()
{
    // non-member function void()
    std::cout << call( std::strlen, "hello world!" ) << '\n' ; // 12

    // lambda void()
    call( [] { std::cout << "nullary lambda returning void\n" ; } ) ; // nullary lambda returning void

    // lambda int(int,int)
    std::cout << call( [] ( int a, int b ) { return a+b ; }, 7, 16 ) << '\n' ; // 23

    // binary function object int(int,int)
    std::cout << call( std::plus<>(), 7, 16 ) << '\n' ; // 23
}

http://coliru.stacked-crooked.com/a/1254d761480a0a7a
Topic archived. No new replies allowed.