#include <functional>
void toggleOk(bool& b) { b = !b; }
void toggleBroken(bool b) { b = !b; }
void toggleInt(int i) { i = !i; }
void tooManyParams(bool b, int i) { i = !b; }
int main()
{
typedef std::function<void(bool&)> CallbackType;
typedef std::function<void(bool)> WrongCallbackType;
CallbackType cb1 = [](bool b) { b = !b; }; // Should throw error - missing reference
CallbackType cb2 = toggleOk; // Ok
CallbackType cb3 = toggleBroken; // Should throw error - missing reference
CallbackType cb4 = toggleInt; // Should throw error - integer instead of bool
WrongCallbackType cb5 = toggleBroken; // Ok
CallbackType cb6 = cb5; // Type checking not even applying between std::functions
CallbackType cb7 = tooManyParams; // Only this statement throws error
return 0;
}
Consider the above example, which is creating a bunch of callbacks that have reference to bool
as a parameter. Except the very last callback cb7
, this code can compile and run just fine, even though most of the functions stored in the callback objects mismatch the reference or type of the parameter.
I've encountered this behavior with VS19/C 20 with the lambda stored in the std::function
, however I've tried this example with two distinct G compilers for Windows, with extra diagnostics enabled and with C 17/C 2a and none reported even a warning.
My question is - is this an expected behavior or is it a bug? Why?
CodePudding user response:
Yes, this is defined behavior from std::function
The std::function
uses a type erasure mechanism to warp almost all kinds of callable objects, and parameterized to the non-const, non-ref non-volatile arguments and the return type of the callable.
You need to use plane typed function pointer to get the expected errors in the code
void toggleOk(bool& b) { b = !b; }
void toggleBroken(bool b) { b = !b; }
void toggleInt(int i) { i = !i; }
void tooManyParams(bool b, int i) { i = !b; }
int main()
{
// typedef std::function<void(bool&)> CallbackType;
// typedef std::function<void(bool)> WrongCallbackType;
using CallbackType = void(*)(bool&);
using WrongCallbackType = void(*)(bool);
CallbackType cb1 = [](bool b) { b = !b; }; // error
CallbackType cb2 = toggleOk; // Ok
CallbackType cb3 = toggleBroken; // error
CallbackType cb4 = toggleInt; // error
WrongCallbackType cb5 = toggleBroken; // Ok
CallbackType cb6 = cb5; // error
return 0;
}
Now in the above CallbackType
and WrongCallbackType
are different types, and will produce the error as you expected.
However, you can only use the function pointer type (as shown above) in the case of lambda, only if it is stateless (do not capture anything).