Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is type checking not happening for std::function?

#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?

like image 235
doomista Avatar asked Aug 29 '21 08:08

doomista


People also ask

What is static type checking in C?

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.

What does the type-checker do?

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.

What is type checking in compiler?

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.

Why do we need error handling in type checker?

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.


1 Answers

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

like image 122
JeJo Avatar answered Oct 26 '22 23:10

JeJo