c++ - 为什么 C++17 中 std::function 的 operator() 会发生变化?

标签 c++ c++14 c++17

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

#include <functional>

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

不要费心对其进行测试,您会得到不一致的结果,因此很难判断这是错误还是故意行为。然而,比较两个草稿(N4140 与 N4527,两者都可以在 github.com/cplusplus/draft 上找到),[func.wrap.func.inv] 有一个显着差异。第 2 段:

Returns: Nothing if R is void, otherwise the return value of INVOKE (f, std::forward(args)..., R).

以上内容已在草稿之间删除。这意味着 lambda 的返回值现在被默默地丢弃。这似乎是一个错误的特征。谁能解释一下原因?

最佳答案

有一个 ridiculous defect in the standard关于 std::function<void(Args...)> .根据标准的措辞,没有(非平凡的)1 使用 std::function<void(Args...)>是合法的,因为没有任何东西可以“隐式转换为”void (甚至没有 void )。

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

一些编译器采用了导致 std::function<void(Args...)> 的错误措辞。完全没用,并且仅将逻辑应用于返回值为 not 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&) 兼容,因为您可以在“无效上下文”中运行它。


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

2 对于 void隐式转换为 void根据标准,它要求声明void x = blah; , 其中 blah是 void 类型的表达式,有效;该语句无效,因为您无法创建 void 类型的变量.

关于c++ - 为什么 C++17 中 std::function 的 operator() 会发生变化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33069029/

相关文章:

c++ - 在 clang 和 gcc 中移动可分配的 lambda

c++ - 解析文件时,我可以将 std::string_view 与 getline 一起使用吗?

c++ - 尝试使用 std::visit 和 lambda 表达式从 std::variant 返回值

c++ - 从没有虚函数的 C++ 类继承

c++ - 检查 vector 中具有特定条件的唯一元素

c++ - 模板变量特化(针对模板模板类)

c++ - 如何专门化/重载模板化类型的模板函数

c++ - 类模板中封闭类型的静态数组

c++ - 在没有 STL 的情况下在 C++ 中创建通用链表

c++ - 上次运行进程 win32 的句柄