2

I want to declare a function that takes as argument a function (or lambda) with a specific prototype.

The first try is:

#include <iostream>
using namespace std;
int test(int (&val)(int)) {
    return val(2);
}
int main() {
    cout << test([](int v){
        return v+100;
    });
    return 0;
}

witch results in error: invalid initialization of non-const reference of type 'int (&)(int)' from an rvalue of type 'main()::<lambda(int)>'

I tried to add const specified to the type but I don't know where exactly so I tried the following that works with GCC (-std=c++14), however I suspect it is illegal since it fails with clang:

int test(const auto& val) {
    return val(2);
}

I know I can use template or function pointers or std::function to achieve the same, then please consider this a didactic question. I want to know what does GCC deduce the type of val to, in the second example above (int test(const auto& val)).

template <typename F>
int test(F val) {
    return val(2);
}

int test(int val(int)) {
    return val(2);
}

int test(std::function<int(int)> val) {
    return val(2);
}
barsdeveloper
  • 930
  • 8
  • 28
  • Sure and they suggest to use templates or std::function, however with GCC compiler it works even without templates. – barsdeveloper Apr 21 '17 at 09:37
  • @cpplearner I think this is the only possible explanation, you should send an answer. Thank you! – barsdeveloper Apr 21 '17 at 10:05
  • [It works just fine by accepting a function pointer](http://rextester.com/JPNGG21655) While conversion of captureless lambda to function pointer is implicit and so is conversion between function reference and function pointer, chaining these conversions together is too much. – Ben Voigt Apr 24 '17 at 02:59

1 Answers1

3

I want to declare a function that takes as argument a function (or lambda) with a specific prototype.

Closures generated by lambda expression have a unique and anonymous type. They could also be generic (i.e. template operator()). There is no "easy" way of declaring a function accepting lambdas with a specific prototype. Your best bet is either using a constrained template parameter with something like std::is_invocable or using some sort of type erasure like function_view.

Note: if your lambda is captureless it is implicitly convertible to a function pointer. E.g.

int test(int (*)(int)) { }

test([](int) -> int {});      // OK
test([i = 0](int) -> int {}); // compile-time error

I want to know what does GCC deduce the type of val to, in the second example.

int test(int val(int))

...is equivalent to...

int test(int (*val)(int))

In fact, having both of them in the same scope results in a redefinition error:

int test(int val(int)) {
    return val(2);
}

int test(int (*val)(int)) {
    return val(2);
}

prog.cc:5:5: error: redefinition of 'test'
int test(int (*val)(int)) {
    ^
prog.cc:1:5: note: previous definition is here
int test(int val(int)) {
    ^
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Sorry, maybe I didn't express myself properly, with second example I mean `int test(const auto& val)`. I don't get warnings – barsdeveloper Apr 21 '17 at 09:43
  • 3
    @biowep, auto can't be used in function parameter lists. To take any type, templates must be used - `template int test(const T& val)` – ralismark Apr 21 '17 at 09:48