开发者

Using Boost.Phoenix's operator ->*

I'm playing around with Phoenix v3 trying to figure out whether we should standardize on it instead of the current mix of Bind and Lambda. From the documentation I got the impression that it should be possible to simplify some expressions.

Currently I'm stuck on the usage of the ->* operator in combination with the STL algos. The following will compile (Visual Studio 2008 SP1) but not give the expected output:

#include <algorithm>
#include <iostream>
#include <vector&g开发者_如何学编程t;
#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;
}

Running this example will print out 'A' twice, both times for the bind-based loops. So here are my questions:

  • What should I change in order to have the operator-based loop actually call A::f?
  • How could I change the double-bind loop using operators?
  • Anyone know why VS2008 is always complaining when you don't specify mem_fn in these cases? I always get warning C4180 (qualifier applied to function type has no meaning; ignored).

Thanks in advance for any insights.


Given:

#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(); };
};

The first one is an easy fix:

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

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

Regarding operator->*, the Phoenix docs clearly state:

The left hand side of the member pointer operator must be an actor returning a pointer type.

Consequently, when given an object (in this case an A&), one must take the address of said object in order to use operator->* -- hence &arg1. (Also, because Phoenix actors are lazy, one must use an extra set of parenthesis in order to obtain the eager functor rather than the lazy functor.)


The second one is not quite as easy, as one cannot use only operators -- because we must have an actor representing a pointer in order to use operator->*, we would need to take the address of the result of B::a, but the result of B::a is an rvalue and taking the address of any rvalue is illegal. We have two options:

  1. Store the result of B::a into a variable, making it an lvalue, and consequently making it legal to take the address of. This can be accomplished using 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)()])
        );
    }
    

    Obviously this is pretty ugly.

  2. Use phoenix::bind instead of operator->*, since it works equally well with references and pointers, obviating the need to take the address of the result of 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)()));
    }
    


I am also not too good at phoenix but I think you cannot use the ->* operator the way you want it to use.
If you change your example to

...
    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)());
...

you will get two times A. In the examples I only found examples with pointers so I guess you can only use the phoenix ->* operator with pointers. Which should be ok as the operator ->* binds to pointers.
From the Spec in 5.5:

The binary operator ->* binds its second operand, which shall be of type “pointer to member of T” (where T is a completely-defined class type) to its first operand, which shall be of type “pointer to T” or “pointer to a class of which T is an unambiguous and accessible base class

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜