使用 Boost.Phoenix 的运算符 ->*

Using Boost.Phoenix's operator ->*

本文关键字:gt 运算符 Boost Phoenix 使用      更新时间:2023-10-16

我正在玩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(限定符应用于函数类型没有意义;忽视)。

提前感谢你的真知灼见

给定:

#include <algorithm>
#include <vector>
#include <iostream>
#include <boost/phoenix.hpp>
struct A
{
    void f() const { std::cout << "An"; };
};
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->*, Phoenix文档明确指出:

成员指针操作符的左侧必须是返回指针类型的参与者。

因此,当给定一个对象(在本例中是A&)时,必须获取该对象的地址才能使用operator->*——因此是&arg1。(另外,由于Phoenix actor是惰性的,因此必须使用额外的圆括号来获得急切函子,而不是惰性函子。)


第二个操作就不那么简单了,因为不能只使用操作符——因为我们必须有一个actor来表示指针才能使用operator->*,我们需要获取B::a结果的地址,但是B::a的结果是右值,获取任何右值的地址都是非法的。我们有两个选项:

  1. B::a的结果存储到一个变量中,使其为左值,从而使获取的地址合法。这可以使用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)()));
    }
    

我也不太擅长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。在示例中,我只找到了带有指针的示例,所以我猜你只能对指针使用凤凰->*操作符。这应该没问题,因为操作符->*绑定了指针。
摘自5.5中的规范:

二进制操作符->*绑定第二个操作数,该操作数必须为of类型"指向T成员的指针"(其中T是一个完全定义的类)类型)到它的第一个操作数,该操作数的类型应为"指向T的指针"或指向一个类的指针,其中T是一个明确的、可访问的基类类