Home > Net >  Why is type checking not happening for std::function?
Why is type checking not happening for std::function?

Time:09-17

#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).

  • Related