我有以下代码,允许我实例化然后调用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
类接受具有不同(但一致)签名的函数?
例如,考虑ChainOfResponsibility<void(int)>
或ChainOfResponsibility<ReturnClass(Argument1Class, Argument2Class)>
。
为了参数,我们假设第二个示例返回链中最后一个成员返回的值,如果链为空,则返回ReturnClass的默认值。
另外,如果STL已经包含了实现此目的的模板类,那么我更愿意在我的本土课程中使用它。
答案 0 :(得分: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;
}
答案 1 :(得分: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;
}