4

please look at the following code

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

int main()
{
    std::function<void(std::string&)> theFunc;
    std::string foo = "0";

    theFunc = [](std::string a) { a = "1";  };  // this compiles but has a different function signature
    theFunc(foo);
    std::cout << "foo should be 1 but is " << foo << std::endl;

    theFunc = [](auto a) { a = "2";  };         // this infers the wrong type for auto(by val not by ref), creates the wrong function signature and compiles 
    theFunc(foo);
    std::cout << "foo should be 2 but is " << foo << std::endl;

    theFunc = [](std::string& a) { a = "3";  };  // this compiles and correctly manipulates the string
    theFunc(foo);
    std::cout << "foo should be 3 and is " << foo << std::endl;

    theFunc = [](auto& a) { a = "4";  };  // this compiles and correctly manipulates the string
    theFunc(foo);
    std::cout << "foo should be 4 and is " << foo << std::endl;

    std::cin.get();
}

In the code example we have one std::function assigned different types of lambdas.

The lambda 3 i understand because the function signature matches.

But lambda 1 creates a different function signature but compiles correctly.

Lambda 2 infers the wrong auto type (by val and not by ref) and compiles correctly.

Is this a feature or a bug? What do i missunderstand regarding the function class / lambdas and the auto type inference?

UPDATE:

Thanks for the answer Handy999 but why is the following not compiling then?

    std::function<void(std::string)> theFunc2;

    theFunc2 = [](std::string& a) { a = "1";  };  // this doesn't compile and has a different function signature
    theFunc2(foo);
2
  • 1
    The fifth case fails because you can't call that lambda with a temporary string (i.e. [](std::string& a) { a = "1"; }("1"); is invalid). std::function preserves that restriction Commented Feb 21, 2019 at 15:45
  • Thanks Caleth, that was it. [](const std::string& a) { } compiled. Commented Feb 21, 2019 at 15:48

1 Answer 1

5

Unlike function pointers, std::function takes everything which can be called as specified. If necessary, it creates a small wrapper function (in the background).

In all cases the code

void smallWrapper(std::string& s) {
    ([](std::string a) { a = "1"; })(s);
}

void smallWrapper2(std::string& s) {
    ([](auto a) { a = "2"; })(s);
}

void smallWrapper3(std::string& s) {
    ([](std::string& a) { a = "3"; })(s);
}

void smallWrapper4(std::string& s) {
    ([](auto& a) { a = "4"; })(s);
}

can be called. auto always deduces the base type, so always to std::string. Thus case 2=case 1 and case 4=case 3. This is what std::function does and what it should do.


For the 5th case, it is really as Caleth pointed out. You cannot call

([](std::string& a) { a = "5"; })("string");

since you cannot bind a reference to a temporary. (Here, the wrapper function would work. So, its not a very good model.) For const references, it works as usual:

([](const std::string& a) { a = "6"; })("string");
Sign up to request clarification or add additional context in comments.

3 Comments

This isn't quite true, because the wrapper preserves the value category of the argument, which is why the updated case fails
That's true. The value category stays.
I personally find this behavior of std::function extremely dangerous. With a simple typo the program manipulates a copy and not the intended object without any compile error.

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.