У меня есть следующий код, который позволяет мне создать экземпляр, а затем вызвать список 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 уже содержит шаблонный класс, который достигает этого, то я предпочел бы использовать его над своим домашним классом.
Ваше конкретное «отбросить все промежуточные результаты» также довольно просто, но я думаю, что это плохая идея.
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;
}
Вам не нужно использовать 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;
}