Henk
Henk

Reputation: 769

std::function accepts lambda functions with arguments of different passing type (by ref, by val)

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);

Upvotes: 4

Views: 219

Answers (1)

Handy999
Handy999

Reputation: 776

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");

Upvotes: 5

Related Questions