шаблоны — Как мне обобщить вызов списка функций в C ++?

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

4

Решение

Ваше конкретное «отбросить все промежуточные результаты» также довольно просто, но я думаю, что это плохая идея.

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

Другие решения

Вам не нужно использовать 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;
}
1