#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?
Static type checking is defined as type checking performed at compile time. It checks the type variables at compile time, which means the type of the variable is known at the compile time. It generally examines the program text during the translation of the program.
The type-checker determines whether these values are used appropriately or not. It checks the type of objects and reports a type error in the case of a violation, and incorrect types are corrected. Whatever the compiler we use, while it is compiling the program, it has to follow the type rules of the language.
Type Checking. A compiler must check that the source program follows both syntactic and semantic conventions of the source language. This checking, called static checking, detects and reports programming errors.
Since type checking has the potential for catching errors in program, it is desirable for type checker to recover from errors, so it can check the rest of the input. Error handling has to be designed into the type system right from the start; the type checking rules must be prepared to cope with errors.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With