以下代码在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段:
返回:如果R为空则无效,否则返回值为INVOKE(f,std :: forward(args)...,R)。
在草稿之间删除了上述内容。这意味着lambda的返回值现在被静默丢弃。这似乎是一种错误。任何人都可以解释这个推理吗?
答案 0 :(得分:21)
std::function<void(Args...)>
有一个ridiculous defect in the standard。根据该标准的措辞,std::function<void(Args...)>
的任何(非平凡) 1 使用都是合法的,因为没有任何东西可以被隐含地转换为&#34; void
(甚至不是void
)。
void foo() {} std::function<void()> f = foo;
在C ++ 14中不合法。糟糕。
有些编译器采用了使std::function<void(Args...)>
完全没用的错误措辞,并且仅将逻辑应用于传入的callables,其中返回值 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&)
兼容,因为你可以在&#34; void context&#34;中运行它。
1 基本上,任何使operator()
合法调用的内容都是不允许的。你可以创建它,你可以销毁它,你可以测试它(并知道它是空的)。你不能给它一个函数,即使是一个与其签名完全匹配的函数,或函数对象或lambda。可笑了。
2 要使void
在标准下被隐含转换为void
,它需要声明void x = blah;
,其中blah
是void类型的表达式,是有效的;该语句无效,因为您无法创建void
类型的变量。