【问题标题】:Why is type checking not happening for std::function?为什么 std::function 没有进行类型检查?
【发布时间】:2021-08-29 08:16:44
【问题描述】:
#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 为参数的reference 回调。除了最后一个回调cb7,这段代码可以编译和运行得很好,即使大多数存储在回调对象中的函数与referencetype不匹配参数。

我在 VS19/C++20 中遇到了这种行为,lambda 存储在 std::function 中,但是我已经使用两个不同的 Windows G++ 编译器尝试了这个示例,启用了额外的诊断功能并使用了 C++ 17/C++2a 甚至没有报告警告。

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

【问题讨论】:

  • 检查大致是:bool b{}; toggleInt(b);,并且可以编译(tooManyParams 除外)。
  • 我看到的方式是你创建一个 std::function 传递一个 ref 参数,然后调用一个没有 ref 的底层函数。这很好,裁判不会改变,一切都很好。如果您调用 cb1、cb2 等,我会在运行时检查。行为完全符合预期。

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


【解决方案1】:

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

std::function 使用type erasure mechanism 扭曲几乎所有类型的可调用对象,并参数化为非常量、非引用、非易失性参数和可调用对象的返回类型。

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

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 的情况下使用函数指针类型(如上图),前提是it is stateless (do not capture anything)

【讨论】:

  • 我有点想知道为什么选择这种行为,我一直认为 std::function 是原始函数指针的 C++ 等价物,显然它做得更多。感谢您的解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 2017-01-17
  • 2019-09-24
相关资源
最近更新 更多