С++ 11 variadic std:: function parameter
Функция с именем test
принимает в качестве параметра std:: function < > .
template<typename R, typename ...A>
void test(std::function<R(A...)> f)
{
// ...
}
Но если я сделаю следующее:
void foo(int n) { /* ... */ }
// ...
test(foo);
Компилятор (gcc 4.6.1) говорит no matching function for call to test(void (&)(int))
.
Чтобы последняя строка test(foo)
компилируется и работает правильно, как я могу изменить функцию test()
? В функции test()
мне нужно f
с типом std:: function < > .
Я имею в виду, есть ли какие-либо шаблонные трюки, позволяющие компилятору определить подпись функции (foo
в примере) и автоматически преобразовать ее в std::function<void(int)>
?
ИЗМЕНИТЬ
Я хочу сделать эту работу для лямбда (как заявленных, так и без гражданства).
Ответы
Ответ 1
Похоже, вы хотите использовать перегрузку
template<typename R, typename ...A>
void test(R f(A...))
{
test(std::function<R(A...)>(f));
}
Эта простая реализация будет принимать большинство, если не все функции, которые вы попытаетесь пройти. Экзотические функции будут отклонены (например, void(int...)
). Больше работы даст вам более общую информацию.
Ответ 2
std::function
реализует интерфейс Callable, т.е. он выглядит как функция, но это не значит, что вам нужно, чтобы вызываемые объекты были std::function
s.
template< typename F > // accept any type
void test(F const &f) {
typedef std::result_of< F( args ) >::type R; // inspect with traits queries
}
Утиная печать - лучшая политика в метапрограммировании шаблонов. При принятии аргумента шаблона быть неспецифичным и просто позволить клиенту реализовать интерфейс.
Если вам действительно нужен std::function
, например, чтобы переназначить эту переменную или что-то сумасшедшее, и вы знаете, что ввод является необработанным указателем функции, вы можете разложить тип указателя необработанных функций и перевести его в std::function
.
template< typename R, typename ... A >
void test( R (*f)( A ... ) ) {
std::function< R( A ... ) > internal( f );
}
Теперь пользователь не может передать std::function
, поскольку он был инкапсулирован внутри функции. Вы могли бы сохранить свой существующий код в качестве другой перегрузки и просто делегировать на это, но будьте осторожны, чтобы простые интерфейсы.
Что касается ямбда с учетом состояния, я не знаю, как справиться с этим делом. Они не разлагаются на указатели функций, и насколько я знаю, типы аргументов не могут быть запрошены или выведены. Эта информация необходима для создания std::function
, к лучшему или к худшему.
Ответ 3
Обычно не рекомендуется принимать std::function
по значению, если вы не используете "двоичное разграничение" (например, динамическая библиотека, "непрозрачный" API), поскольку, как вы только что видели, они играют хаос с перегрузкой. Когда функция действительно принимает значение std::function
по значению, то часто бремя вызывающего объекта заключается в том, чтобы построить объект, чтобы избежать проблем с перегрузкой (если функция вообще перегружена).
Так как вы написали шаблон, скорее всего, вы не используете std::function
(как тип параметра) для преимуществ стирания стилей. Если то, что вы хотите сделать, это проверить произвольные функторы, вам понадобятся некоторые черты. Например. Boost.FunctionTypes имеет такие черты, как result_type
и parameter_types
. Минимальный, функциональный пример:
#include <functional>
#include <boost/function_types/result_type.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/function_type.hpp>
template<typename Functor>
void test(Functor functor) // accept arbitrary functor!
{
namespace ft = boost::function_types;
typedef typename ft::result_type<Functor>::type result_type;
typedef ft::parameter_types<Functor> parameter_types;
typedef typename boost::mpl::push_front<
parameter_types
, result_type
>::type sequence_type;
// sequence_type is now a Boost.MPL sequence in the style of
// mpl::vector<int, double, long> if the signature of the
// analyzed functor were int(double, long)
// We now build a function type out of the MPL sequence
typedef typename ft::function_type<sequence_type>::type function_type;
std::function<function_type> function = std::move(functor);
}
Как последнее замечание, я не рекомендую интроспективные функторы (т.е. prodding для их типа результата и типов аргументов) в общем случае, поскольку это просто не работает для полиморфных функторов. Рассмотрим несколько перегруженных operator()
: тогда нет "канонического" типа результата или типов аргументов. С С++ 11 лучше "с нетерпением" принимать любые функции или ограничивать их с помощью таких методов, как SFINAE или static_assert
в зависимости от потребностей, а затем (когда доступны параметры) использовать std::result_of
для проверки тип результата для заданного набора аргументов. Случай, когда сдерживание фронта желательно, - это когда целью является сохранение функторов, например, контейнер std::function<Sig>
.
Чтобы получить представление о том, что я имею в виду в предыдущем абзаце, достаточно проверить этот фрагмент с помощью полиморфных функторов.
Ответ 4
Это старый, и я не могу найти многого на одной теме, поэтому я подумал, что буду идти дальше и добавить примечание.
Скомпилированный на GCC 4.8.2, следующие работы:
template<typename R, typename... A>
R test(const std::function<R(A...)>& func)
{
// ...
}
Однако вы не можете просто вызвать его, передав указатели, лямбды и т.д. Однако следующие два примера работают с ним:
test(std::function<void(int, float, std::string)>(
[](int i, float f, std::string s)
{
std::cout << i << " " << f << " " << s << std::endl;
}));
также:
void test2(int i, float f, std::string s)
{
std::cout << i << " " << f << " " << s << std::endl;
}
// In a function somewhere:
test(std::function<void(int, float, std::string)>(&test2));
Недостаток этих функций должен выглядеть довольно явно: вы должны явно объявить для них функцию std::, которая может выглядеть немного уродливо.
Тем не менее, я бросил это вместе с кортежем, который расширился, чтобы вызвать входящую функцию, и он работает, просто требуя немного больше явно сказать, что вы делаете, вызывая тестовую функцию.
Пример кода, включающего в себя кортеж, если вы хотите играть с ним: http://ideone.com/33mqZA