为什么 std::function 没有进行类型检查?

5
#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 存储在 std::function 中的 lambda 表达式时遇到了这种情况,但是我尝试了两个不同的 G++ 编译器(用于 Windows),并启用了额外的诊断,同时使用了 C++17/C++2a,却没有报告任何警告。
我的问题是 - 这是一种预期的行为还是一个错误?为什么?

1
检查大致是:bool b{}; toggleInt(b);,并且它可以编译(除了 tooManyParams)。 - Jarod42
我认为的方法是创建一个std::function,将其传递给一个引用参数,然后调用一个没有引用的底层函数。这样做很好,引用不会被更改,一切仍然很好。我在运行时检查了一下,如果你调用cb1、cb2等,行为与预期完全一致。 - Pepijn Kramer
1个回答

6

是的,这是从 std::function 中定义的行为。

std::function 使用 类型抹消机制 来包装几乎所有类型的可调用对象,并将其参数化为不带 const、ref 和 volatile 的参数和可调用对象的返回类型。

您需要使用纯类型函数指针来获得代码中预期的错误。

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 表达式中只能使用函数指针类型(如上所示),前提是它是无状态的(不捕获任何内容)


1
我有点想知道为什么选择了这种行为,我一直认为 std::function 是 C++ 中原始函数指针的等价物,显然,它的功能要多得多。谢谢解释。 - doomista

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接