如何调用函数,传递函子的返回值(可能为 void)

How to call a function, passing the return value (possibly void) of a functor?

本文关键字:返回值 void 何调用 调用 函数      更新时间:2023-10-16

给定一个函数,该函数调用模板化函数参数并调用另一个对返回值执行某些操作的函数:

template <typename T>
void doSomething(T &&) {
    // ...
}
template <typename T_Func>
void call(T_Func &&func) {
    doSomething(func());
}

如何将其扩展为与返回void的函子一起工作?理想情况下,我想添加一个重载void doSomething() { ... },如果func的返回类型为 void ,则调用该。

目前,如果func返回void,这只会导致error: invalid use of void expression

Ideone的工作示例

我认为您可以创建一个结构助手来使用重载,运算符,或多或少如下所示:

#include <type_traits>
#include <utility>
struct my_void { };
struct my_type { };
template <class T, typename std::enable_if<std::is_void<T>::value>::type* = nullptr>
my_void operator,(T, my_type) { return {}; }
template <class T, typename std::enable_if<!std::is_void<T>::value>::type* = nullptr>
T &&operator,(T &&val, my_type) { return std::forward<T>(val); }
template <typename T>
void doSomething(T &&) {
}
template <typename T_Func>
void call(T_Func &&func) {
    doSomething((func(), my_type{}));
}
int main() {
    auto func1 = []() -> bool { return true; };
    auto func2 = []() -> void { };
    call(func1);
    call(func2);
}

[现场演示]

编辑:

感谢Piotr Skotnicki和Holt(他们指出第一次重载实际上永远不会被触发,并提出了该方法的简化版本):

#include <type_traits>
#include <utility>
struct dumb_t { };
template <class T>
T &&operator,(T &&val, dumb_t) { return std::forward<T>(val); }
template <typename T>
void doSomething(T &&) {
}
template <typename T_Func>
void call(T_Func &&func) {
    doSomething((func(), dumb_t{}));
}
int main() {
    auto func1 = []() -> bool { return true; };
    auto func2 = []() -> void { };
    call(func1);
    call(func2);
}

[现场演示]

doSomething()接受参数,参数不能为空。

因此,为了使它正常工作,您还需要一个不带参数的重载doSomething()。这将是第一步:

template <typename T>
void doSomething(T &&) {
    // ...
}
void doSomething()
{
}

所以,你必须先这样做,然后才能开始。

您也可以为参数提供默认值,以防函子返回 void ; 并且仍然使用单个模板。这是另一种可能性,以下解决方案可以很容易地调整,以一种明显的方式,来处理这个问题。

这里需要发生的是返回void的函子的call()专用化。遗憾的是,函数不能部分专用化,因此需要一个帮助程序类:

#include <utility>
template <typename T>
void doSomething(T &&) {
    // ...
}
void doSomething()
{
}
// Helper class, default implementation, functor returns a non-void value.
template<typename return_type>
class call_do_something {
public:
    template<typename functor>
    static void call(functor &&f)
    {
        doSomething(f());
    }
};
// Specialization for a functor that returns a void.
//
// Trivially changed to call the template function instead, with
// a default parameter.
template<>
class call_do_something<void> {
public:
    template<typename functor>
    static void call(functor &&f)
    {
        f();
        doSomething();
    }
};
// The original call() function is just a wrapper, that selects
// the default or the specialized helper class.
template <typename T_Func>
void call(T_Func &&func) {
    call_do_something<decltype(func())>::call(std::forward<T_Func>(func));
}
// Example:
void foobar()
{
    call([] { return 1; });
    call([] {});
}

您可以提供一对额外的重载帮助程序函数(在下面的示例中名为 callDispatch)来根据需要调度调用,这消除了对部分专用化的需要,从而消除了帮助程序类的需要。它们通过对它们采用的std::function对象使用不同的签名规范来重载(此处为实时代码):

#include <iostream>
#include <functional>
int func1()
{
    return 1;
}
void func2()
{
}
template <typename T>
void doSomething(T &&) 
{
    std::cout << "In param version" << std::endl;
    // ...
}
void doSomething()
{
    std::cout << "In no-param version" << std::endl;
    // ...
}
template <typename R, typename ... Args>
void callDispatch(std::function<R(Args...)> &&f)
{
    doSomething(f());
}
template <typename ... Args>
void callDispatch(std::function<void(Args...)> &&f)
{
    f();
    doSomething();
}
template <typename T>
void call(T &&func) {
    callDispatch(std::function<std::remove_reference_t<T>>(func));
}
int main() {
    call(func1);
    call(func2);
}

一种精简变体是给方法一个函数作为参数。然后,计算方法中的表达式,看看是否执行了任何操作。一般来说,这通常是不好的做法,因为您通常可以推断它如何返回东西以及何时需要它。