Reputation: 1319
Came across an interesting issue today started by my own typo. I created a lambda that takes in a reference to a struct and incorrectly set it to a std::function that receives it's argument by value.
Here's a more concise version:
#include <functional>
struct InputStruct
{
int i;
InputStruct(): i(1){}
};
void function_rcv(std::function<bool(InputStruct)> & func_ref)
{
InputStruct in;
func_ref(in);
}
int main()
{
std::function<bool(InputStruct)> my_func = [](InputStruct & in)->bool{return in.i==1;};
function_rcv(my_func);
}
Checking with godbolt shows this compiles successfully with MSVC, but fails for both Clang and GCC.
Interestingly enough, using a primitive instead of a struct fails compilation on all three compilers.
Is this a bug in the MSVC compiler?
Upvotes: 6
Views: 307
Reputation: 29970
In summary: it is not a compiler bug. MSVC accepts this code because of its default non-conforming behavior, but it can be made standard-conforming with a switch.
First of all, I need to clarify std::function
's one aspect: it accepts a function (in general, Callable) which signature is not a perfect match, but the parameters can be converted. Consider:
using intFn = void (int);
void fn(short);
intFn *a = fn; // doesn't compile
std::function<intFn> b = fn; // compiles!
Here, intFn
a function type which has an int
parameter, while the function fn
has a short
parameter. The simple function pointer a
, cannot be set to point to fn
, as the type of the parameter differ (int
vs short
). But, std::function
allows this, so b
can be set to point to fn
.
In your example, std::function
has an InputStruct
parameter by value, while the lambda has a non-const lvalue reference InputStruct &
. When std::function
std::forward
s its parameter, it becomes an xvalue, which cannot be bound to the lambda's lvalue reference parameter. That's why standard conforming compilers don't accept this code.
Why does MSVC accept this code? Because it has non-conforming behavior by default: it allows binding class temporaries (and xvalues) to non-const lvalue references. You can disable this behavior with /Zc:referenceBinding
(or the older /Za
option). If you use this switch, MSVC rejects your example.
Upvotes: 6