Я написал этот базовый класс для хранения std::function<T> в std::vector<T>, и у меня есть два бесплатных шаблона функций foo() и bar(), которые оба возвращают void и принимают std::vector<T> в качестве параметра. В настоящее время они делают то же самое для простоты; но предположим, что для справки в будущем они будут выполнять другие вычисления или задачи. Пока это то, что я придумал:
#include <vector>
#include <functional
#include <iostream>
#include <exception>
template<typename T>
class MyClass {
private:
std::vector<std::function<void(std::vector<T>)>> myFuncs_;
public:
MyClass() = default;
void addFunc( std::function<void(std::vector<T>)> func ) {
myFuncs_.push_back(func);
}
std::function<void(std::vector<T>)> caller(unsigned idx) {
return myFuncs_.at(idx);
}
};
template<typename T>
void foo(std::vector<T> data) {
std::cout << "foo() called:\n";
for (auto& d : data)
std::cout << d << " ";
std::cout << '\n';
}
template<typename T>
void bar(std::vector<T> data) {
std::cout << "bar() called:\n";
for (auto& d : data)
std::cout << d << " ";
std::cout << '\n';
}
int main() {
try {
MyClass<int> myClass;
std::vector<int> a{ 1,3,5,7,9 };
std::vector<int> b{ 2,4,6,8,10 };
std::function<void(std::vector<int>)> funcA = std::bind(foo<int>, a);
std::function<void(std::vector<int>)> funcB = std::bind(bar<int>, b);
myClass.addFunc( funcA );
myClass.addFunc( funcB );
myClass.caller(0)(a);
myClass.caller(1)(b);
} catch( std::runtime_error& e ) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
И этого достаточно, чтобы вывести:
foo() called:
1 3 5 7 9
bar() called:
2 4 6 8 10
Вот что я хотел бы знать: есть ли способ (-а) упростить этот код; некоторые из синтаксиса выглядят избыточными, например: в основной функции после того, как я создал экземпляр шаблона моего класса и создал std::vector со значениями, я затем создаю экземпляр std::function<T>, используя std::bind с функцией, которую я хочу затем добавить в свой вектор класса. Затем, привязав их и добавив в контейнер моего класса, я вызываю функцию класса для индексации функции, которую я хочу вызвать, с помощью std::functionsoperator(). Однако функция ожидает std::vector<T>, поэтому похоже, что я передаю этот vector<T> несколько раз, как вы можете видеть из этой части моего кода выше.
// std::vector<T> being passed to std::bind
std::function<void(std::vector<int>)> funcA = std::bind(foo<int>,a);
myClass.addFunc( funcA );
myClass.caller(0)(a); // Again std::vector<T> being passed but to `std::function`'s operator because the stored function requires this parameter.
Можно ли это упростить или это просто семантика того, как работают std::function и std::bind? Если это можно упростить, будет ли это сделано внутри самого класса, чтобы упростить задачу для пользователя, или это будет сделано со стороны пользователя?
И вместо функции-получателя, которая возвращает функцию, возможно, вместо нее будет функция «вызова» (или, возможно, даже оператор вызова)?
@Someprogrammerdude Я думал об этих строках, но я пока не очень эффективно использую std::function и std::bind, а синтаксис загромождает мой мыслительный процесс; ха-ха .... С другой стороны, уже немного поздно, я мог бы сделать перерыв и поработать над этим завтра.
@Someprogrammerdude пример type-aliases и функции call были бы признательны.
@FrancisCugler Вместо std::bind() вы должны использовать захватывающие лямбда-функции.
Здесь вам не нужен bind: foo<int> уже имеет соответствующий тип ... Это станет более очевидным, если вы вместо этого используете лямбда; это эквивалентно тому, что произвел бы bind: funcA = [&]() { foo<int>(a); } - обратите внимание на несовпадающую подпись ...
@Aconcagua Я пробовал лямбду; но я думаю, что это единственное, чего мне не хватало, - reference в пункте захвата.
@FrancisCugler Но вам это не нужно, ни привязка, ни лямбда: Просто имейте: std::function<void(std::vector<int>) f = foo<int>;: f хочет, чтобы что-то возвращало void и принимало вектор, foo делает это.
@Aconcagua хм, MSVC вызывает у меня проблемы с попыткой назначить лямбду для std::function ...
@FrancisCugler Из-за несоответствия типа см. Предыдущие комментарии ...
@Aconcagua Я думаю, это то, чего мне не хватало. После замены на ... = foo<int>; и ... = bar<int>; я думаю, что начинаю немного лучше понимать std::function. Я объявляю funcA и funcB как std::function<T>, но при их назначении вы можете назначить им адрес функции; таким образом, pointer to a function.
@FrancisCugler ... или лямбда, или объект-функтор - пока подпись совпадает (лямбда) / предоставляется соответствующий operator() (функтор). std::function - это универсальный контейнер.
@Aconcagua На самом деле MSVC здесь не ошибается, потому что функция std::binded будет игнорировать любые избыточные аргументы вместо ошибки компиляции. Это ожидаемо. @FrancisCugler Ваш код не работает, он работает случайно. a в myClass.caller(0)(a); вообще не действует. Можно попробовать заменить на myClass.caller(0)(b);, вывод должен быть точно таким же.
@liliscent Здесь я подумал, что .caller(0) даст индекс vector, используя его функцию at(), а затем, поскольку это извлекает std::function, который хранится с индексом 0 или 1, который использует (a) или (b) после того, как он затем будет использовать std::functionoperator() позволяя call указать указатель на функцию, а также передать ему необходимые параметры ... Или мне что-то не хватает?
@FrancisCugler У вас серьезное недоразумение по поводу std::bind, это нелегко объяснить, потому что я не знаю, почему вы неправильно это поняли. Я предлагаю вам внимательно прочитать Примеры в документации. en.cppreference.com/w/cpp/utility/functional/bind
@FrancisCugler Повторная попытка с лямбда: [&](std::vector<int> other) { foo<int>(a); } Важная вещь: bind(foo<int>, a) вызывает foo с a вместо other.





Вы неправильно используете std::bind, например, в
std::function<void(std::vector<int>)> funcA = std::bind(foo<int>, a);
Вы уже связали аргумент a с foo<int>, что означает, что любой параметр, который вы передадите в funcA, не вступит в силу, приведенное выше эквивалентно
std::function<void()> funcA = std::bind(foo<int>, a);
Для простоты кода, если вы хотите придерживаться std::bind, вы можете просто иметь оператора вызова на MyClass, который вызывает все зарегистрированные std::function:
template<typename T>
class MyClass {
private:
std::vector<std::function<void()>> myFuncs_;
public:
MyClass() = default;
template <class... F, std::enable_if_t<(sizeof...(F) > 0), bool> = true>
void addFunc(F&&... func) {
((myFuncs_.push_back(std::forward<F>(func))), ...);
}
void operator()() const {
for (auto& fn : myFuncs_) {
fn();
}
}
};
auto funcA = std::bind(foo<int>, a);
auto funcB = std::bind(bar<int>, b);
myClass.addFunc(funcA, funcB);
myClass();
используя ту же идею, вы можете в любой момент переключиться на лямбды.
Использование вывода типа компилятора с
auto- хорошее начало для упрощения. Как и использование псевдонимов типов в классе для часто используемых типов (например, типаstd::function).