Ответ 1
std::function
имеет одну фиксированную подпись. Это был выбор дизайна, а не жесткое требование. Написание псевдо std::function
, поддерживающее несколько подписей, не сложно:
template<class...Sigs>
struct functions;
template<>
struct functions<> {
functions()=default;
functions(functions const&)=default;
functions(functions&&)=default;
functions& operator=(functions const&)=default;
functions& operator=(functions&&)=default;
private:
struct never_t {private:never_t(){};};
public:
void operator()(never_t)const =delete;
template<class F,
std::enable_if_t<!std::is_same<std::decay_t<F>, functions>{}, int>* =nullptr
>
functions(F&&) {}
};
template<class S0, class...Sigs>
struct functions<S0, Sigs...>:
std::function<S0>,
functions<Sigs...>
{
functions()=default;
functions(functions const&)=default;
functions(functions&&)=default;
functions& operator=(functions const&)=default;
functions& operator=(functions&&)=default;
using std::function<S0>::operator();
using functions<Sigs...>::operator();
template<class F,
std::enable_if_t<!std::is_same<std::decay_t<F>, functions>{}, int>* =nullptr
>
functions(F&& f):
std::function<S0>(f),
functions<Sigs...>(std::forward<F>(f))
{}
};
использование:
auto f = [](int a = 3) {std::cout << a << std::endl; };
functions<void(int), void()> fs = f;
fs();
fs(3);
Это создаст отдельную копию вашей лямбды для каждой перегрузки. Возможно даже иметь различные лямбды для разных перегрузок при тщательном литье.
Вы можете написать тот, который этого не делает, но в принципе потребовалось бы переопределить std::function
внутреннее состояние fancier.
Более продвинутая версия вышеизложенного позволит избежать использования линейного наследования, поскольку это приводит к коду O (n ^ 2) и O (n) рекурсивной глубине шаблона в количестве подписей. Сбалансированное наследование двоичного дерева падает до 0 (n lg n) генерируемого кода и глубины O (lg n).
Версия промышленной прочности будет хранить переданную в лямбда один раз, использовать небольшую оптимизацию объектов, иметь ручную псевдо-таблицу, которая использовала двоичную стратегию наследования для отправки вызова функции и хранить указатели на отправленные функции в упомянутой псевдо-виртуальной таблице, Это займет O (# подписи) * sizeof (function pointer) пробел для каждого класса (не для каждого экземпляра) и будет использовать примерно столько же служебных данных для каждого экземпляра, сколько std::function
.
Но это немного для SO-сообщения, нет?