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

时间:2015-10-11 19:11:50

标签: 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段:

  

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

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

1 个答案:

答案 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。然后他们断定传递一个函数返回intstd::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类型的变量。