Как выполнить y → λx.yx «поднятие» функции в функтор?

Редактировать: Краткое описание: возможно ли создание шаблонного типа, оператор которого () вызывает произвольную функцию, указанную в качестве параметра шаблона?


Рассмотрим (шаблонную) функцию

template <typename T> std::operator-(const T& lhs, const T& rhs)

в стандартной библиотеке C ++, в <functional> мы иметь следующая конструкция (игнорирование constexpr и void по умолчанию в C ++ 14, а также удобное наследование binary_function):

  template<typename T>
struct minus {
T operator()(const T& lhs, const T& rhs) const {
return operator-(lhs, rhs)
};
};

Итак, «функтор» (на языке стандартной библиотеки) std::minus это своего рода «снятие» функции operator-, И у нас так же есть std::plus, std::multiplies и так далее.

Теперь предположим, что я хотел добиться такого же эффекта для произвольной функции. Вот как я мог бы сделать это с помощью макроса (для случая с известным числом параметров, скажем, 2)

#define CONCAT(s1,s2) s1##s2
#define EXPAND_THEN_CONCAT(s1,s2) CONCAT(s1,s2)
#define DEFINE_FUNCTOR(f, ret_type, param1_type, param2_type) \
struct EXPAND_THEN_CONCAT(f,_functor)  { \
ret_type operator()(param1_type x, param2_type y) const { \
return f(x, y)  \
}; \
};

… но, конечно, я хочу сделать это способом C ++ 11 (или способом C ++ 14, если это поможет). Как бы я это сделал?

У меня была идея передать f в качестве параметра шаблона как-то; но как сделать так, чтобы оператор () имел ту же сигнатуру, что и f (и с маркером const)? Для фиксированной подписи я предположил, что это не будет так сложно, и код должен выглядеть так std::minus — но в целом я не могу понять, как это будет сделано.

Я думал о том, чтобы попытаться сделать что-то похожее на то, что я использовал бы при «поднятом вызове» функций:

template<typename F, typename... Parameters>
inline void invoke(const F& f, Parameters... parameters) {
f(parameters...);
}

но, опять же, это не сработает — это даст мне исполнение, которое мне нравится, но не подпись, которая мне нравится; и мне нужна эта подпись …

2

Решение

std::minus это тип. Это тип, который имеет operator() перегрузка, которая просто так случается вызвать operator- на аргументы это дается.

Вы спрашиваете, есть ли способ создать тип, который может вызвать любую произвольную функцию? Это потребует, чтобы вы как-то указали функцию для вызова этого типа. Единственный способ предоставить параметры тип через аргументы шаблона.

И пока нетиповые параметры шаблона могут быть указатели на функции, они не будут работать как std::minus, который вызывает operator- через разрешение перегрузки и тому подобное. Указатели на функции указывают на конкретные функции, а не на наборы перегрузки.

Кажется, что вам нужно иметь возможность указать идентификатор и получить обратно объект, который при вызове будет вызывать этот идентификатор точно так же, как если бы вы вызывали его напрямую. Это включает разрешение перегрузки и тому подобное.

C ++ как язык не может этого сделать. Бумага P0119 (PDF) это предложение добавить такую ​​языковую функцию, хотя она не обрабатывает функции-члены. Неясно, куда это делось в Коне в 2015 году, даже если оно было представлено.

Самое близкое, что вы можете получить — это доработка вашего макроса через лямбды C ++ 14:

#define CREATE_FUNCTOR(f) \
[=](auto&& ...args) -> decltype(auto) \
{ \
return f(std::forward<decltype(args)>(args)...); \
}

Причина использования лямбды здесь в том, что, если вы используете это в функции-члене, лямбда сможет захватывать this если f случается, функция-член. Он также будет считаться другом вмещающего типа.

Конечно, это создает значение, а не тип.

4

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

Это то, что вы хотите?

#include <iostream>
#include <utility>

template <typename Sig, Sig& f>
struct func_wrapper;

template <typename R, typename... Args, R(&f)(Args...)>
struct func_wrapper<R(Args...), f> {
template <typename... Ts>
R operator ()(Ts&&... xs) const {
return f(std::forward<Ts>(xs)...);
}
};

int func(int x) {
return x;
}

int main() {
func_wrapper<decltype(func), func> f;
std::cout << f(1) << std::endl;
}
1

По вопросам рекламы [email protected]