Можно ли создавать функционально-локальные замыкания до C ++ 11? - PullRequest
4 голосов
/ 20 апреля 2011

С C ++ 11 мы получаем лямбды и возможность создавать функции / функторы / замыкания на лету там, где они нам действительно нужны, а не там, где они на самом деле не принадлежат.
В C ++98/03, хороший способ создания функционально-локальных функторов / замыканий был бы следующим:

struct{
  void operator()(int& item){ ++item; }
}foo_functor;
some_templated_func(some_args, foo_functor);

К сожалению, вы не можете использовать локальные типы для шаблонов (Visual Studio позволяет это с языкомрасширения включены).Тогда мой ход прошел следующим образом:

struct X{
  static void functor(int& item){ ++item; }
};
some_templated_func(some_args, &X::functor);

Очевидная проблема заключается в том, что вы не можете сохранить любое состояние, поскольку локальные структуры / классы не могут иметь статических членов.
Мой следующийМы думали, что для решения этой проблемы было использовано сочетание std::bind1st и std::mem_fun и нестатических методов и переменных, но, к сожалению, std::mem_fun почему-то задыхается от std::mem_fn(&X::functor), что опять-таки может быть из-за того, что локальная структура / классы не могут бытьиспользуется в шаблонах:

// wanted, not working solution
struct X{
  int n_;
  X(int n) : n_(n) {}
  void functor(int& item) const { item += n_; }
};
X x(5);
some_templated_func(some_args,std::bind1st(std::mem_fun(&X::functor),&x));

Сбой в VC9 и VC10 (с /Za, отключенные языковые расширения) со следующей ошибкой

error C2893: Failed to specialize function template 'std::const_mem_fun1_t<_Result,_Ty,_Arg> std::mem_fun(_Result (_Ty::* )(_Arg) const)'
With the following template arguments:
'void'
'main::X'
'int &'

или в gcc 4.3.4 с этой ошибкой

error: no matching function for call to ‘mem_fun(void (main()::X::*)(int&))’

Как ни странно, VC9 / VC10 по-прежнему задыхается в приведенном выше примере, даже с включенными языковыми расширениями:

error C2535: 'void std::binder1st<_Fn2>::operator ()(int &) const' : member function already defined or declared

Итак, функциональность, указанная в названии, так или иначе достижима?Или я делаю ошибку в последнем примере, как я использую std::bind1st или std::mem_fun?

Ответы [ 3 ]

4 голосов
/ 20 апреля 2011

bind1st работает только для двоичных функций, и в целом это очень ограничено. mem_fn работает только с нестатическими функциями-членами; для вашего приложения вы хотели бы ptr_fun.

Действительно лучшим инструментом для работы в C ++ 03 является Boost Bind, или я продемонстрирую здесь с tr1::bind, который (на мой взгляд) более переносим.

#include <tr1/functional>
#include <iostream>
#include <algorithm>

using namespace std::tr1::placeholders;

int nums[] = { 1, 2, 4, 5, 6, 8 };

int main() {
    struct is_multiple {
        static bool fn( int mod, int num ) { return num % mod == 0; }
    };

    int *n = std::find_if( nums, nums + sizeof nums/sizeof*nums,
                        std::tr1::bind( is_multiple::fn, 3, _1 ) );

    std::cout << n - nums << '\n';
}
2 голосов
/ 20 апреля 2011

Да, вы можете, но вам придется реализовать один или несколько виртуальных методов, объявленных в интерфейсе.

template<typename Arg, typename Result>
struct my_unary_function
{
    virtual Result operator()(Arg) = 0;
};

template<typename Arg, typename Result>
struct my_unary_functor
{
    my_unary_function<Arg, Result> m_closure;
    my_unary_functor(my_unary_function<Arg, Result> closure) : m_closure(closure) {}
    Result operator()(Arg a) { return m_closure(a); }
};

template<typename T, TFunctor>
void some_templated_function( std::vector<T> items, TFunctor actor );

Затем вы можете определить и использовать локальное закрытие:

void f()
{
    std::vector<int> collection;

    struct closure : my_unary_function<int, int>
    {
        int m_x, m_y;
        closure(int x, int y) : m_x(x), m_y(y) {}
        virtual int operator()(int i) const { cout << m_x + m_y*i << "\t"; return (m_x - m_y) * i; }
    };

    some_templated_function( collection, my_unary_functor<int,int>(closure(1, 5)) );
}

Кредит @ Omnifarious за это улучшение (my_unary_functor не требуется):

void f()
{
    std::vector<int> collection;

    struct closure : my_unary_function<int, int>
    {
        int m_x, m_y;
        closure(int x, int y) : m_x(x), m_y(y) {}
        virtual int operator()(int i) const { cout << m_x + m_y*i << "\t"; return (m_x - m_y) * i; }
    };

    // need a const reference here, to bind to a temporary
    const my_unary_functor<int,int>& closure_1_5 = my_unary_functor<int,int>(closure(1, 5))
    some_templated_function( collection, closure_1_5 );
}
0 голосов
/ 20 апреля 2011

Если бы это было выполнимо в C ++ 03, почему C ++ 0x ввел лямбды? Есть причина, по которой существуют лямбды, и это потому, что связывание и все другие решения C ++ 03 ужасно сосут

...