Редактировать: Краткое описание: возможно ли создание шаблонного типа, оператор которого () вызывает произвольную функцию, указанную в качестве параметра шаблона?
Рассмотрим (шаблонную) функцию
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...);
}
но, опять же, это не сработает — это даст мне исполнение, которое мне нравится, но не подпись, которая мне нравится; и мне нужна эта подпись …
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
случается, функция-член. Он также будет считаться другом вмещающего типа.
Конечно, это создает значение, а не тип.
Это то, что вы хотите?
#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;
}