Использование оператора Boost.Phoenix -> * - PullRequest
2 голосов
/ 21 июля 2011

Я играю с Phoenix v3, пытаясь выяснить, должны ли мы стандартизировать его вместо текущего сочетания Bind и Lambda. Из документации у меня сложилось впечатление, что некоторые выражения можно упростить.

В настоящее время я застрял на использовании оператора -> * в сочетании с алгоритмами STL. Следующее будет скомпилировано (Visual Studio 2008 SP1), но не даст ожидаемый результат:

#include <algorithm>
#include <iostream>
#include <vector>
#include <boost/mem_fn.hpp>
#include <boost/phoenix.hpp>
using namespace std;
using namespace boost;
using namespace boost::phoenix;
using namespace boost::phoenix::arg_names;

struct A
{
  void f() const { cout << "A"; };
};
struct B
{
  A a() { return A(); };
};

int main()
{
  vector<A> va(1);
  for_each(va.begin(), va.end(), bind(mem_fn(&A::f), arg1));
  for_each(va.begin(), va.end(), arg1->*&A::f);

  vector<B> vb(1);
  for_each(vb.begin(), vb.end(), bind(mem_fn(&A::f), bind(mem_fn(&B::a), arg1)));
  return 0;
}

Выполнение этого примера выведет «A» дважды, оба раза для циклов на основе привязки. Итак, вот мои вопросы:

  • Что я должен изменить, чтобы операторный цикл действительно вызывал A :: f?
  • Как я могу изменить цикл двойного связывания, используя операторы?
  • Кто-нибудь знает, почему VS2008 всегда жалуется, если вы не указываете mem_fn в этих случаях? Я всегда получаю предупреждение C4180 (квалификатор, примененный к типу функции, не имеет значения; игнорируется).

Заранее спасибо за любые идеи.

Ответы [ 2 ]

4 голосов
/ 21 июля 2011

Дано:

#include <algorithm>
#include <vector>
#include <iostream>
#include <boost/phoenix.hpp>

struct A
{
    void f() const { std::cout << "A\n"; };
};

struct B
{
    A a() const { return A(); };
};

Первый способ легко исправить:

int main()
{
    using boost::phoenix::arg_names::arg1;

    std::vector<A> va(1);
    std::for_each(va.begin(), va.end(), (&arg1->*&A::f)());
}

Относительно operator->*, документы Феникса четко указывают :

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

Следовательно, когда дан объект (в данном случае A&), нужно взять адрес указанного объекта, чтобы использовать operator->* - следовательно, &arg1. (Кроме того, поскольку актеры Феникса ленивы, нужно использовать дополнительный набор скобок, чтобы получить активный функтор, а не ленивый функтор.)


Второй вариант не так прост, так как нельзя использовать только операторы - потому что у нас должен быть актер, представляющий указатель , чтобы использовать operator->*, нам нужно взять адрес результата B::a, но результат B::a является rvalue, и взятие адреса любого rvalue является недопустимым. У нас есть два варианта:

  1. Сохраните результат B::a в переменной, сделав его lvalue, и, следовательно, сделав законным получение адреса. Это можно сделать с помощью phoenix::let:

    int main()
    {
        using boost::phoenix::let;
        using boost::phoenix::arg_names::arg1;
        using boost::phoenix::local_names::_a;
    
        std::vector<B> vb(1);
        std::for_each(
            vb.begin(),
            vb.end(),
            (let(_a = (&arg1->*&B::a)())[(&_a->*&A::f)()])
        );
    }
    

    Очевидно, это довольно уродливо.

  2. Используйте phoenix::bind вместо operator->*, поскольку он одинаково хорошо работает со ссылками и указателями, устраняя необходимость в получении адреса результата B::a:

    int main()
    {
        using boost::phoenix::bind;
        using boost::phoenix::arg_names::arg1;
    
        std::vector<B> vb(1);
        std::for_each(vb.begin(), vb.end(), bind(&A::f, (&arg1->*&B::a)()));
    }
    
1 голос
/ 21 июля 2011

Я тоже не слишком хорош в Phoenix, но я думаю, что вы не можете использовать оператор -> * так, как вы хотите.
Если вы измените свой пример на

...
    vector<A*> va;
    va.push_back(new A);
    for_each(va.begin(), va.end(), bind(mem_fn(&A::f), arg1));
    for_each(va.begin(), va.end(), (arg1->*&A::f)());
...

, вы будетеполучить два раза A. В примерах я нашел только примеры с указателями, поэтому, я думаю, вы можете использовать только оператор phoenix -> * с указателями.Что должно быть в порядке, поскольку оператор -> * связывается с указателями.
Из спецификации в 5.5:

Бинарный оператор -> * связывает свой второй операнд, который должен иметь тип «указатель»члену T »(где T - полностью определенный тип класса) своему первому операнду, который должен иметь тип« указатель на T »или« указатель на класс, для которого T является однозначным и доступным базовым классом

...