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

时间:2017-01-04 13:21:34

标签: c++ c++11

给定一个调用模板化函数参数的函数,并调用另一个函数来执行返回值:

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

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

如何将其扩展为使用返回void的仿函数?理想情况下,如果void doSomething() { ... }的返回类型为func,我想添加一个被调用的重载void

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

Working example on Ideone

4 个答案:

答案 0 :(得分:3)

我认为你可以创建一个struct helper来使用重载的,运算符或多或少这样:

#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);
}

[live demo]

修改

感谢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);
}

[live demo]

答案 1 :(得分:1)

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

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

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

void doSomething()
{
}

所以,在你开始行动之前,你必须先做这件事。

如果仿函数返回void,您也可能希望为参数提供默认值;并且仍然使用单个模板。这是另一种可能性,并且可以通过显而易见的方式轻松调整以下解决方案来处理。

这里需要做的是call()的特殊化,用于返回void的仿函数。不幸的是,函数不能部分专门化,因此需要一个辅助类:

#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([] {});
}

答案 2 :(得分:0)

你可以提供一对额外的重载辅助函数(在我下面的例子中命名为callDispatch)来根据需要调度调用,这样就不需要部分特化,因此不需要辅助类。他们使用std::function对象的不同签名规范(实时代码here)来重载它们:

#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);
}

答案 3 :(得分:-1)

一个精益变体将是给该方法一个函数作为参数。然后,您评估方法内的表达式,看看是否做了什么。一般来说,这通常是不好的做法,因为你通常可以推断它是如何返回东西以及何时需要它。