c++ - 为什么 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
.甚至没有人报告警告。
我的问题是 - 这是预期的行为还是错误?为什么?
解决方案
是的,这是定义的行为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;
}
现在在上面CallbackType
和WrongCallbackType
是不同的类型,并且会如你预期的那样产生错误。
但是,你只能在 lambda 的情况下使用函数指针类型(如上所示),前提是它是无状态的(不捕获任何东西)。
推荐阅读
- ssis - SSIS并行执行不运行序列容器
- python - 当我在 python 中调用 OpenGl 时出现错误
- openstack - 从 openstack 环境收集指标并在 grafana 中显示
- java - 来自 ArrayList<> 的实体对象上的 JAVA 泛型
- timestamp - 在 aws athena 中按日期时间查询解析 alb 日志时出错
- javascript - 显示和隐藏选项通常被捕获到表中的单个 Id
- android - 不要使用 Android 导航组件在 backstack 中包含片段
- d3.js - 应用交叉过滤器时,有没有办法为 dc.js 中的热图中未选中的框设置默认颜色?
- azureservicebus - 是否可以将 Azure Service Bus 的指标信息转发到 Azure Application Insights?
- javascript - 创建一个可以作为参数传递给 swiper JS 的对象