5

I have the following code:

auto get_functor = [&](const bool check) {
    return  [&](const foo& sr)->std::string {
        if(check){
            return "some string";
        }
        return "another string"
    };
};
run(get_functor(true));

The run function signature:

void run(std::function<std::string(const foo&)> func);

I am getting the following error which is not so clear for me:

error C2440: 'return' : cannot convert from 'main::<lambda_6dfbb1b8dd2e41e1b6346f813f9f01b5>::()::<lambda_59843813fe4576d583c9b2877d7a35a7>' to 'std::string (__cdecl *)(const foo &)'

P.S. I am on MSVS 2013

Edit:

if I edit the code by replacing auto with the real type:

std::function<std::string(const foo&)> get_functor1 = [&](const bool check) {
    return  [&](const foo& sr)->std::string {
        if (check) {
            return "some string";
        }
        return "another string";
    };
};
run(get_functor1(true));

I am getting another error:

error C2664: 'std::string std::_Func_class<_Ret,const foo &>::operator
()(const foo &) const' : cannot convert argument 1 from 'bool' to
'const foo &'

Which is totally messed up!

12
  • 2
    A side note: you should capture by value in the returned lambda: return [=](const foo& sr) .... Since here you are getting a reference that will be outlived. Then, what's the full code example that leads to that error? Commented Apr 24, 2017 at 6:49
  • 2
    Not reproducible ideone.com/gpz109 Commented Apr 24, 2017 at 6:50
  • Works ideone.com/5ZZ7TQ Commented Apr 24, 2017 at 6:51
  • 1
    It appears from the error message that the function expects a raw function pointer, not an std::function object Commented Apr 24, 2017 at 6:51
  • @Rerito Some of them are non-copyable.. this is another problem to deal with now! Commented Apr 24, 2017 at 6:52

3 Answers 3

5

I was able to reproduce the same error on VS 2013 with the following MVCE:

#include <iostream>
#include <functional>
#include <string>

struct foo {};

std::string run(std::function<std::string(foo const&)> f) {
    return f(foo());
}

int main() {

    auto get_functor = [&](bool const check) {
        return [=](foo const&) -> std::string { // Line of the compiler error
            if (check) {
                return "CHECK!";
            }
            else {
                return "NOT CHECK!";
            }
        };
    };

    std::cout << run(std::function<std::string(foo const&)>(get_functor(true)));
    return 0;
}

I then get the error:

Error   1   error C2440: 'return' : cannot convert from 'main::<lambda_1bc0a1ec72ce6dc00f36e05599609bf6>::()::<lambda_4e0981efe0d720bad902313b44329b79>' to 'std::string (__cdecl *)(const foo &)'

The problem lies with MSVC's inability to handle returned lambdas: when you do not specify the return type, it is trying to decay it into a regular function pointer. This fails because your lambda does capture elements!

Moreover, your fix is wrong since std::function<std::string(foo const&)> is not the type of get_functor but rather the type you want to return from it.

Forcing the embedding into an std::function of the returned lambda directly in get_functor will resolve your issue:

auto get_functor = [&](bool const check) -> std::function<std::string(foo const&)> {
    return [=](foo const&) -> std::string {
        if (check) {
            return "some string";
        } else {
            return "another string";
        }
    };
};
std::cout << run(get_functor(true));
Sign up to request clarification or add additional context in comments.

4 Comments

I don't have a copy of MSVC2013 to hand to check (who does??) but would it help to add a trailing return type of decltype(auto)? converting to std::function seems like a bit of a heavyweight solution.
@RichardHodges That's a C++14 feature IIRC so it shouldn't work. Just in case, I tested what you suggested and it failed with error: error C3553: decltype expects an expression not a type. I am well aware that the type erasure behind an std::function comes with a price but the OP uses it just after with his run function. We're just doing it one step ahead here.
Ok, thanks for trying it. In that case I have proposed a functor-based version, which of course allows us to explicitly name the types.
@Rerito Stupid me! My fix is totally rubbish. Thank you very much it is working now :)
3

Frankly, I sometimes wonder whether complex lambdas are worth the bother.

Breaking it down to a functor object of a known type will always work:

#include <functional>
#include <iostream>
#include <string>


struct foo
{
};

struct foo_functor{

    foo_functor(bool check) : check(check) {}

    std::string operator()(const foo&) const
    {
        if (check) {
            return "some string";
        }
        return "another string";
    }
    const bool check;
};

auto make_foo_functor(bool check) -> foo_functor
{
    return foo_functor { check };
}

void run(std::function<std::string(const foo&)> func)
{
    foo f;
    auto s = func(f);
    std::cout << s << std::endl;
}

int main()
{

    std::cout << "c++ version: " << __cplusplus << std::endl;

    auto get_functor = [&](const bool check) -> foo_functor
    {
        return make_foo_functor(check);
    };

    run(get_functor(true));
}

2 Comments

Thanks it seems good solution since I am able to template foo_functor so the bool check will be compile time which I was searching for from the begining (typical X-Y problem :D )
That's more elegant and it can avoid the type erasure induced by std::function. I'ld say that's the way to go (compared to the 'hotfix' in my answer).
1

From your edit, get_functor1 is a lambda that takes a single bool argument and returns another lambda, but you try to convert get_functor1 to this function type:

std::function<std::string(const foo&)>

That is not compatible. Instead, if you want to avoid auto type deduction, you should use:

std::function<std::function<std::string(const foo&)>(bool)>

That is, a function that takes a bool and returns another function which takes a const foo & and returns a std::string

get_functor1 then becomes:

std::function<std::function<std::string(const foo&)>(bool)> get_functor1 = [&](const bool check)
{
    return  [&](const foo& sr)->std::string {
        if (check) {
            return "some string";
        }
        return "another string";
    };
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.