замена унарного функтора актором boost::phoenix

У меня есть приложение Visual Studio 2008 C++, в котором я хотел бы заменить унарный функтор лямбда-выражением boost::phoenix.

В моем случае у меня есть список объектов, содержащих строку. Я хочу удалить все объекты со строкой, не совпадающей с указанной. Итак, я использую такой алгоритм:

struct Foo
{
    std::string my_type;
};

struct NotMatchType
{
    NotMatchType( const std::string& t ) : t_( t ) { };
    bool operator()( const Foo& f ) const
    {
        return f.my_type.compare( t_ ) != 0;
    };
    std::string t_;
};

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector< Foo > list_of_foo;

    /*populate with objects*/

    std::string some_type = "some type";

    list_of_foo.erase(
        std::remove_if( list_of_foo.begin(),
                        list_of_foo.end(),
                        NotMatchType( some_type ) ),
        list_of_foo.end() );

    return 0;
}

Это прекрасно работает. Но я хотел бы немного почистить свой код, избавиться от функтора NotMatchType и заменить его простым лямбда-выражением, подобным этому:

using boost::phoenix::arg_names::arg1;

list_of_foo.erase(
    std::remove_if( list_of_foo.begin(),
                    list_of_foo.end(),
                    arg1.my_type.compare( some_type ) != 0 ),
    list_of_foo.end() );

очевидно, это не работает.

Я также пробовал: ( arg1->*&Foo::my_type ).compare( some_type ) != 0

Что мне нужно сделать, чтобы boost:phoenix:actor выглядел как объект Foo?


person PaulH    schedule 29.08.2011    source источник


Ответы (2)


Учитывая две строки lhs и rhs, тогда lhs == rhs определяется как семантически эквивалентный lhs.compare(rhs) == 0. Другими словами, то, что делает ваш функтор, эквивалентно выполнению f.my_type != t_.

Имея это в виду, вы можете выразить то, что хотите, с помощью Phoenix следующим образом:

bind(&Foo::my_type, arg1) =! ref(some_type)

Для протокола: вы звонили участнику compare по поводу актера Феникса. Поскольку этот участник принадлежит std::string, это не то, что вам нужно. Я могу заставить работать следующее:

typedef int (std::string::*compare_type)(std::string const&) const;
compare_type compare = &std::string::compare;
bind(compare, bind(&Foo::my_type, arg1), "") != 0;

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

Для дальнейшего использования я предпочитаю лямбда-выражения при вызове перегруженного члена:

auto compare = [](std::string const& lhs, std::string const& rhs)
{ return lhs.compare(rhs); };
// bind that functor and use it as a Phoenix actor etc
person Luc Danton    schedule 29.08.2011
comment
arg1 не представляет тип указателя, поэтому использование operator->* для него некорректно. (&arg1)->*&Foo::my_type != ref(some_type) должно работать. - person ildjarn; 29.08.2011
comment
lambdas (являющиеся частью С++ 11), к сожалению, недоступны в Visual Studio 2008. - person PaulH; 30.08.2011
comment
@ildjarn Я не слежу. operator->* перегружен; код, который я показал, компилируется с моей стороны. - person Luc Danton; 30.08.2011
comment
@Luc: из документы: Левая часть оператора указателя на элемент должна быть субъектом, возвращающим тип указателя. В этом случае возвращается левый размер (arg1) ссылочный тип, а не тип указателя. - person ildjarn; 30.08.2011
comment
@Luc Danton - @ildjarn прав. Хотя приложение скомпилируется, я начинаю получать ошибки. Run-Time Check Failure #2 - Stack around the variable 'that' was corrupted. в boost/proto/generate.hpp(223) - person PaulH; 30.08.2011
comment
Заменены все экземпляры ->* на bind. (&arg1)->*&T::member тоже возможен, но, на мой вкус, слишком синтаксически шумный. - person Luc Danton; 30.08.2011

Использовать std::string::compare() напрямую из Phoenix довольно некрасиво, так как он перегружен и нам нужно взять его адрес:

phx::bind(
    static_cast<int (std::string::*)(std::string const&) const>(
        &std::string::compare
    ),
    phx::cref(some_type),
    phx::bind(&Foo::my_type, arg1)
) != 0

Однако, если мы последуем подсказке Люка и просто сравним равенство объектов, это станет более управляемым:

phx::cref(some_type) != phx::bind(&Foo::my_type, arg1)
person ildjarn    schedule 29.08.2011
comment
Это чем-то отличается от использования boost::bind? boost::bind( &Foo::my_type, _1 ) != boost::cref( some_type )? Кроме того, необходимо ли использовать cref, чтобы он не выполнял копирование? - person PaulH; 30.08.2011
comment
@PaulH: Да, boost::bind будет работать в этом контексте, но std::bind и std::tr1::bind - нет. Да, копия будет сделана, если ref или cref не используются. - person ildjarn; 30.08.2011