Подтвердить что ты не робот

С++ 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)>?

ИЗМЕНИТЬ

Я хочу сделать эту работу для лямбда (как заявленных, так и без гражданства).

4b9b3361

Ответ 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