为什么c++ 17中std::函数的operator()发生了变化?

Why does operator() change for std::function in C++17?

本文关键字:发生了 变化 operator 函数 c++ std 为什么      更新时间:2023-10-16

以下代码在c++ 14中被认为是非法的,但在c++ 17中是合法的:

#include <functional>
int main()
{
    int x = 1729;
    std::function<void (int&)> f(
        [](int& r) { return ++r; });
    f(x);
}

不要费心去测试它,你会得到不一致的结果,这会让你很难判断这是一个bug还是故意的行为。然而,比较两个草案(N4140和N4527,两者都可以在github.com/cplusplus/draft上找到),[function .wrap. function .inv]有一个显著的区别。第二款:

返回:如果R为空则无返回值,否则调用INVOKE (f, std::forward(args)…右).

上述内容已在两次草案之间删除。这意味着lambda的返回值现在被静默地丢弃了。这似乎是一个错误的功能。有人能解释一下原因吗?

标准中关于std::function<void(Args...)>有一个可笑的缺陷。根据标准的措辞,没有(非琐碎的)1使用std::function<void(Args...)>是合法的,因为没有任何东西可以"隐式转换"为void(甚至void)。

void foo() {} std::function<void()> f = foo;在c++ 14中是不合法的。哎呀。

一些编译器采用了使std::function<void(Args...)>完全无用的糟糕措辞,并且只将逻辑应用于返回值为而不是 void的传入可调用对象。然后他们得出结论,将返回int的函数传递给std::function<void(Args...)>(或任何其他非void类型)是非法的。他们没有把它带到逻辑的终点,也没有禁止返回void的函数(std::function要求对于完全匹配的签名没有特殊情况:适用相同的逻辑)

其他编译器只是忽略了void返回类型的糟糕措辞。

缺陷基本上是调用表达式的返回类型必须隐式地转换为std::function签名的返回类型(参见上面的链接了解更多细节)。在标准下,void不能隐式转换为void 2

所以缺陷被解决了。std::function<void(Args...)>现在接受任何可以用Args...调用的东西,并丢弃返回值,正如许多现有编译器实现的那样。我推测这是因为(A)语言设计者从来没有打算限制,或者(B)为std::function提供一种丢弃返回值的方法是需要的。

std::function从不要求参数或返回值的精确匹配,只是兼容性。如果传入参数可以隐式地从签名参数转换为返回类型,并且返回类型可以隐式地转换为返回类型,那么它就很高兴了。

在许多直观的定义下,int(int&)类型的函数与签名void(int&)兼容,因为您可以在"void上下文中"运行它。


1基本上,任何使operator()合法调用的东西都是不允许的。您可以创建它,可以销毁它,可以测试它(并且知道它是空的)。您不能给它一个函数,即使是与它的签名完全匹配的函数,也不能给它一个函数对象或lambda。可笑。

2要将void隐式地转换为void,则要求语句void x = blah;有效,其中blah为void类型的表达式;该语句无效,因为您不能创建类型为void的变量。