使用 `std::function` 调用非 void 函数

发布于 2025-01-06 10:22:22 字数 479 浏览 1 评论 0原文

不久前,我使用 std::function 非常像这样:

std::function<void(int)> func = [](int i) -> int { return i; };

基本上,我这样做是因为我想在 std::function 中存储不同的函数对象,但我不想限制这些函数的返回类型。由于这似乎有效,所以我就这么做了。但我不相信它可以安全使用,而且我还没有找到任何有关它的文档。有谁知道这种用法是否合法?或者更一般地说,可以安全地分配给 std::function 的对象的规则是什么?

编辑

为了澄清起见,我关心的问题是 lambda 函数返回一个 int,而 func 声明为返回类型 void。我不确定这是否可以,特别是在调用 func() 后。

A while ago I used std::function pretty much like this:

std::function<void(int)> func = [](int i) -> int { return i; };

Basically, I did this because I wanted to store different function objects in a std::function, but I didn't want to restrict the return types of these functions. Since this seemed to work, I went with it. But I'm not convinced that it is safe to use, and I haven't been able to find any documentation on it. Does anyone know whether this usage is legitimate? Or more generally, what the rules are for the object which can safely be assigned to a std::function?

Edit

For clarification, the issue I'm concerned with is that the lambda function returns an int, while func is declared with return type void. I'm not sure if this is OK, especially once a call to func() is made.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不喜欢何必死缠烂打 2025-01-13 10:22:22

您的代码有未定义的行为。它可能会或可能不会按您的预期工作。它具有未定义行为的原因是由于 20.8.11.2.1 [func.wrap.func.con]/p7:

要求: F 应为CopyConstructible。对于参数类型 ArgTypes 和返回类型 Rf 应可调用 (20.8.11.2)。

要使 f 对于返回类型 R 可调用,f 必须返回可隐式转换为 std::function 返回类型的内容 (在您的情况下为 void)。并且 int 不能隐式转换为 void

我希望您的代码适用于大多数实现。然而,在至少一个实现(libc++)上,它无法编译:

test.cpp:7:30: error: no viable conversion from 'int (int)' to 'std::function<void (int)>'
    std::function<void(int)> ff = f;
                             ^    ~

具有讽刺意味的是,这种行为的基本原理源于另一个问题

另一个问题提出了 std::function 使用的问题。该问题的解决方案涉及让实现在编译时强制执行 Requires: 子句。相反,此问题的解决方案是禁止实施强制执行Requires:子句。

Your code has undefined behavior. It may or may not work as you expect. The reason it has undefined behavior is because of 20.8.11.2.1 [func.wrap.func.con]/p7:

Requires: F shall be CopyConstructible. f shall be Callable (20.8.11.2) for argument types ArgTypes and return type R.

For f to be Callable for return type R, f must return something implicitly convertible to the return type of the std::function (void in your case). And int is not implicitly convertible to void.

I would expect your code to work on most implementations. However on at least one implementation (libc++), it fails to compile:

test.cpp:7:30: error: no viable conversion from 'int (int)' to 'std::function<void (int)>'
    std::function<void(int)> ff = f;
                             ^    ~

Ironically the rationale for this behavior stems from another SO question.

The other question presented a problem with std::function usage. The solution to that problem involved having the implementation enforce the Requires: clause at compile time. In contrast, the solution to this question's problem is forbidding the implementation from enforcing the Requires: clause.

鹊巢 2025-01-13 10:22:22

您的用例已根据标准明确定义。

您正在从可调用对象构造一个 std::function[1]

§20.8.11.2.1/7:

模板函数(F f);

要求:F 应是可复制构造的。对于参数类型 ArgTypes,f 应是可调用的 (20.8.11.2)
并返回类型 R。

那么你的 f 是可调用的吗?

§20.8.11.2/2 说:

类型为 F 的可调用对象 f 对于参数类型 ArgTypes 是可调用的
如果表达式 INVOKE (f, declval()..., R) 被视为未计算的操作数,则返回类型 R
(第 5 条)格式良好 (20.8.2)。

INVOKE 的定义如下:

§20.8.2

  1. 定义INVOKE (f, t1, t2, ..., tN)如下:
    ...处理成员函数/var指针的东西...
    f(t1, t2, ..., tN) 在所有其他情况下。

  2. INVOKE (f, t1, t2, ..., tN, R) 定义为 INVOKE (f, t1, t2, ..., tN) 隐式转换为 R

由于任何类型都可以隐式转换为 void,因此您的代码应该可以使用符合标准的编译器。 正如下面的 litb 所指出的,不存在隐式转换转换为 void 所以这没有很好的定义。

[1]:我认为 lambda 在这里算作一个可调用对象,尽管我没有这方面的参考。您的 lambda 也可以用作函数指针,因为它不捕获上下文

Your use case is well-defined according to the standard.

You are constructing a std::function from a callable object[1]

§20.8.11.2.1/7:

template<class F> function(F f);

Requires: F shall be CopyConstructible. f shall be Callable (20.8.11.2) for argument types ArgTypes
and return type R.

So is your f callable?

§20.8.11.2/2 says:

A callable object f of type F is Callable for argument types ArgTypes
and return type R if the expres- sion INVOKE (f, declval<ArgTypes>()..., R), considered as an unevaluated operand
(Clause 5), is well formed (20.8.2).

And the definition of INVOKE says:

§20.8.2

  1. Define INVOKE (f, t1, t2, ..., tN) as follows:
    ... stuff dealing with member function/var pointers ...
    f(t1, t2, ..., tN) in all other cases.

  2. Define INVOKE (f, t1, t2, ..., tN, R) as INVOKE (f, t1, t2, ..., tN) implicitly converted to R.

And since any type can be implicitly converted to void, your code should be fine with a standards-conforming compiler. As pointed out by litb below, there isn't an implicit conversion to void so this isn't well defined.

[1]: I think the lambda counts as a callable object here, although I don't have a reference for that. Your lambda could also be used as a function pointer as it captures no context

叹倦 2025-01-13 10:22:22

这看起来对于匿名函数来说可能没问题。

引用自 http://www.alorelang.org/release/0.5/doc/std_function .html (这不是来自 C++ 标准库,但看起来他们正在使用类似的东西绑定到 C++)

函数对象只能通过函数定义、匿名函数表达式或使用点 (.) 运算符访问绑定方法来创建。

另一种可能实现的方法是将函数指针存储在 auto 中,如下所示:http://en。 wikipedia.org/wiki/Anonymous_function(C++ 部分)

This looks like it may be ok for anonymous functions.

Quote from http://www.alorelang.org/release/0.5/doc/std_function.html (this is not from the C++ standard library, however it looks like they are using something similar in there bindings down to C++)

Function objects can only be created with a function definition, an anonymous function expression or by accessing a bound method using the dot (.) operator.

Another way this could possibly be done is by storing the function pointer in auto as seen here: http://en.wikipedia.org/wiki/Anonymous_function (C++ section)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文