26

以下代码在 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 为 void,则返回任何内容,否则返回 INVOKE (f, std::forward(args)..., R) 的值。

以上内容在草稿之间被删除。这意味着 lambda 的返回值现在被默默地丢弃。这似乎是一个错误的特征。任何人都可以解释其中的原因吗?

4

1 回答 1

21

大约的标准有一个荒谬的缺陷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。他们没有把它带到逻辑结束并禁止函数返回voidstd::function对于完全匹配的签名,要求没有特殊情况:同样的逻辑适用。)

void其他编译器只是忽略了返回类型案例中的错误措辞。

缺陷基本上是调用表达式的返回类型必须隐式转换为std::function的签名的返回类型(有关更多详细信息,请参见上面的链接)。并且在标准下,void不能隐式转换为void2

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

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

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


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

2为了在标准下void隐式转换void,它要求声明void x = blah;,其中blah是一个void类型的表达式,是有效的;该语句无效,因为您无法创建 type 的变量void

于 2015-10-30T17:22:22.190 回答