首页 > 解决方案 > 为什么 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;
}

考虑上面的例子,它创建了一堆作为参数引用的回调。bool除了最后一个回调之外cb7,这段代码可以编译和运行得很好,即使存储在回调对象中的大多数函数与参数的引用类型不匹配。

我在 VS19/C++20 中遇到了这种行为,lambda 存储在std::function.甚至没有人报告警告。

我的问题是 - 这是预期的行为还是错误?为什么?

标签: c++functionc++11c++17std-function

解决方案


是的,这是定义的行为std::function

使用类型擦除机制来扭曲几乎所有类型的可调用对象,并参数化为非常量、非引用、非易失性参数和可调用对象的返回类型std::function

您需要使用平面类型的函数指针来获取代码中的预期错误

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;
}

现在在上面CallbackTypeWrongCallbackType是不同的类型,并且会如你预期的那样产生错误。

但是,你只能在 lambda 的情况下使用函数指针类型(如上所示),前提是它是无状态的(不捕获任何东西)


推荐阅读