2

我正在玩 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,我应该更改什么?
  • 如何使用运算符更改双绑定循环?
  • 任何人都知道为什么在这些情况下不指定 mem_fn 时 VS2008 总是抱怨?我总是收到警告 C4180(应用于函数类型的限定符没有意义;被忽略)。

提前感谢您的任何见解。

4

2 回答 2

4

鉴于:

#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. (另外,由于 Phoenix 的 Actor 是惰性的,因此必须使用一组额外的括号才能获得 Eager 仿函数而不是惰性仿函数。)


第二个不太容易,因为不能只使用运算符——因为我们必须有一个表示指针的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)()));
    }
    
于 2011-07-21T18:43:21.783 回答
1

我也不太擅长 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 是一个明确且可访问的基类

于 2011-07-21T11:35:35.053 回答