Функция bindParameter с переменными шаблонами в C ++ 11 - PullRequest
3 голосов
/ 17 ноября 2011

Я пытаюсь написать простую функцию для преобразования объекта std :: function <> при привязке последнего параметра (ов).Вот что у меня есть:

template<typename R, typename Bind, typename ...Args> std::function<R (Args...)> bindParameter (std::function<R (Args..., Bind)> f, Bind b)
{
    return [f, b] (Args... args) -> R { return f (args..., b); };
}

И вот как я бы хотел это использовать:

int blub (int a, int b)
{
    return a * b;
}

// ...

int main ()
{
    std::function<int (int, int)> f1 (blub);

    // doesn't work
    std::function<int (int)> f2 = bindParameter (f1, 21);

    // works
    std::function<int (int)> f3 = bindParameter<int, int, int> (f1, 21);

    return f2 (2);
}

... так что в этом примере основная функция должна возвращать 42Проблема в том, что gcc (4.6), по-видимому, неправильно определяет типы параметров шаблона, первая версия выдает следующие ошибки:

test.cpp:35:58: error: no matching function for call to 'bindParameter(std::function<int(int, int)>&, int)'
test.cpp:35:58: note: candidate is:
test.cpp:21:82: note: template<class R, class Bind, class ... Args> std::function<R(Args ...)> bindParameter(std::function<R(Args ..., Bind)>, Bind)

Но, на мой взгляд, параметры очевидны.Или этот тип вывода не предусмотрен стандартом или еще не реализован в gcc?

Ответы [ 2 ]

3 голосов
/ 18 ноября 2011

Вы не можете использовать std::function в качестве выведенного параметра шаблона функции. Удержание не может работать таким образом, поскольку нет правил для соответствия int(*)(int, int) std::function<int(int, int)>. (Учтите также, что для any std::function<Signature> существует конструктор, принимающий int(*)(int, int), даже если в большинстве случаев это приводит к ошибке при создании экземпляра.)

В общем случае проблематично обнаружить сигнатуру функтора. Даже решение KennyTM имеет ограничения: оно обнаруживает сигнатуру мономорфных функторов и подобных им функций, но не работает для полиморфных функторов (например, с перегруженными operator()) или функторов с суррогатными функциями вызова (даже в мономорфном случае).

Однако возможно полностью обойти проблему обнаружения подписи благодаря decltype (или, что эквивалентно, std::result_of), и я бы рекомендовал это сделать. Следовательно, вариант ответа KennyTM:

template<typename Functor, typename Bound>
struct bind_last_type {
    Functor functor;
    Bound bound;

    template<typename... Args>
    auto operator()(Args&&... args)
    -> typename std::result_of<Functor&(Args..., Bound)>::type
    // equivalent:
    // -> decltype( functor(std::forward<Args>(args)..., std::move(bound)) )
    { return functor(std::forward<Args>(args)..., std::move(bound)); }
};

template<typename Functor, typename Bound>
bind_last_type<
    typename std::decay<Functor>::type
    , typename std::decay<Bound>::type
>
bind_last(Functor&& functor, Bound&& bound)
{ return { std::forward<Functor>(functor), std::forward<Bound>(bound) }; }
1 голос
/ 17 ноября 2011

Не уверен насчет логического вывода, но он работает, если я просто определяю шаблонный объект функции.

template <typename FType, typename LastArgType>
struct BindLastHelper
{
    FType _f;
    LastArgType _last_arg;

    template <typename... Args>
    typename utils::function_traits<FType>::result_type
        operator()(Args&&... args) const
    {
        return _f(std::forward<Args>(args)..., _last_arg);
    }
};

template<typename FType, typename LastArgType>
BindLastHelper<FType, LastArgType> bindParameter (FType f, LastArgType b)
{
    return BindLastHelper<FType, LastArgType>{f, b};
}

Примечание:

  • utils::function_traits взято из https://github.com/kennytm/utils/blob/master/traits.hpp. std::result_of нельзя использовать, поскольку вы не передаете указатель на функцию.
  • Подтверждение концепции: http://ideone.com/ux7YY (здесь для простоты я просто переопределил result_of.)
...