4

我有以下代码允许我实例化然后调用void()函数列表。

(如果您希望编译和运行此代码,我正在使用https://github.com/philsquared/Catch进行单元测试)。

#include "catch.hpp"

#include <functional>
#include <vector>

class ChainOfResponsibility : public std::vector<std::function<void()> >, public std::function<void()>
{
public:
    void operator()() const
    {
        for(std::vector<std::function<void()> >::const_iterator it = begin(); it != end(); ++it) {
            (*it)();
        }
    }
};

TEST_CASE("ChainOfResponsibility calls its members when invoked")
{
    bool test_function_called = false;
    std::function<void()> test_function = [&]()
    {
        test_function_called = true;
    };

    ChainOfResponsibility object_under_test;

    object_under_test.push_back(test_function);
    object_under_test();

    REQUIRE(test_function_called);
}

我的问题是如何模板ChainOfResponsibility类以接受具有不同(但一致)签名的函数?

例如,考虑 aChainOfResponsibility<void(int)>或 a ChainOfResponsibility<ReturnClass(Argument1Class, Argument2Class)>

为了论证,假设第二个示例返回链中最后一个成员返回的值,或者如果链为空,则返回 ReturnClass 的默认值。

此外,如果 STL 已经包含实现此目的的模板类,那么我更愿意使用它而不是我自己开发的类。

4

2 回答 2

7

您具体的“丢弃所有中间结果”也相当简单,但我认为这是一个坏主意。

template<typename Ret, typename ... Args>
class ChainOfResponsibility
{
    std::vector<std::function<Ret(Args...)> > chain;
public:
    Ret operator()(Args ... args) const
    {
        Ret value;
        for(auto & func : chain) {
            value = func(args...);
        }
        return value;
    }
};

void必须自己治疗

template<typename ... Args>
class ChainOfResponsibility<void, Args...>
{
    std::vector<std::function<void(Args...)> > chain;
public:
    void operator()(Args ... args) const
    {
        for(auto & func : chain) {
            func(args...);
        }
    }
};

请注意,从std::类型派生是一个坏主意,尤其是std::function,它是类型擦除可调用对象,而不是“所有可调用对象的基础”。您可以简单地提供一个operator()

改进非无效情况的选项:

    // fold the results
    template <typename BinaryFunction>
    Ret operator()(Args ... args, BinaryFunction add, Ret init) const
    {
        for(auto & func : chain) {
            init = add(init, func(args...));
        }
        return init;
    }

    // return a vector
    template <typename BinaryFunction>
    std::vector<Ret> operator()(Args ... args) const
    {
        std::vector<Ret> results(chain.size());
        for(auto & func : chain) {
            results.push_back(func(args...));
        }
        return results;
    }
于 2017-08-18T08:47:51.063 回答
1

你不需要使用std::function作为基类,使用std::vector就足够了。模板ChainOfResponsibility可以使用相同的模板参数列表,std::function如下所示:

#include <iostream>
#include <string>
#include <functional>
#include <vector>

template<typename>
class ChainOfResponsibility;

template<typename R, typename... Args>
class ChainOfResponsibility<R(Args...)> :
        public std::vector<std::function<R(Args...)>> {
public:
    R operator()(const Args&... args) {
        R result {};

        for(auto it = this->begin(); it != this->end(); ++it)
            result = (*it)(args...);

        return result;
    }
};

int main() {
    ChainOfResponsibility<std::string(int, int)> tester;

    tester.push_back([](int a, int b)->std::string {
        return std::to_string(a + b);
    });

    std::cout << tester(4, 2) << std::endl;
}

无论如何,std::vector对于您描述的问题,仅使用就足够了。如果重载的内容operator()没什么特别的,可以把我上面的例子改成如下:

int main() {
    std::vector<std::function<std::string(int, int)>> tester;

    tester.push_back([](int a, int b)->std::string {
        return std::to_string(a + b);
    });

    std::string result;

    for(auto& test_fn : tester)
        result = test_fn(4, 2);

    std::cout << result << std::endl;
}

您还可以编写函数模板而不是重载operator()

template<typename R, typename... Args>
R perform(const std::vector<std::function<R(Args...)>>& functions,
        const Args&... args) {
    R result {};
    for(auto& test_fn : functions)
        result = test_fn(4, 2);
    return result;
}
于 2017-08-18T08:59:51.767 回答