为什么operator()在C++ 17中为std :: function改变?

use*_*082 26 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);
}
Run Code Online (Sandbox Code Playgroud)

不要费心去测试它,你会得到不一致的结果,因此很难说它是一个bug还是故意的行为.但是,比较两个草稿(N4140和N4527,两者都可以在github.com/cplusplus/draft上找到),[func.wrap.func.inv]有一个显着的区别.第2段:

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

在草稿之间删除了上述内容.这意味着lambda的返回值现在被静默丢弃.这似乎是一种错误.任何人都可以解释这个推理吗?

Yak*_*ont 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...)>完全没用的错误措辞,并且仅将逻辑应用于传入的callables,其中返回值不是 void.然后他们得出的结论是非法的传递函数返回intstd::function<void(Args...)>(或任何其它非void型).他们没有把它带到逻辑端并禁止函数返回void(std::function对于完全匹配的签名,要求没有特殊情况:相同的逻辑适用.)

其他编译器只是忽略了void返回类型情况下的错误措辞.

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

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

std::function从来没有要求的参数或者返回值精确匹配,只是兼容性.如果传入的参数可以隐式转换 - 来自签名参数,并且返回类型可以隐式转换为返回类型,那么它很高兴.

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


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

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