C++ 使用Boost.Phoenix';s运算符->*

C++ 使用Boost.Phoenix';s运算符->*,c++,boost,boost-phoenix,C++,Boost,Boost Phoenix,我正在与Phoenix v3玩游戏,试图弄清楚我们是否应该在其上进行标准化,而不是当前的Bind和Lambda的混合。从文档中我得到的印象是,应该可以简化一些表达式 目前,我一直在使用->*操作符与STL算法相结合。以下内容将编译(Visual Studio 2008 SP1),但不会给出预期的输出: #include <algorithm> #include <iostream> #include <vector> #include <boost/me

我正在与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;
}
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
使用名称空间boost;
使用名称空间boost::phoenix;
使用名称空间boost::phoenix::arg_名称;
结构A
{
void f()常量{cout*&A::f);
向量vb(1);
对于每个(vb.begin()、vb.end()、bind(mem_-fn(&A::f)、bind(mem_-fn(&B::A)、arg1));
返回0;
}
运行此示例将打印两次“A”,对于基于绑定的循环,两次都是。 下面是我的问题:

  • 为了让基于操作符的循环真正调用A::f,我应该更改什么
  • 如何使用运算符更改双绑定循环
  • 有人知道为什么VS2008在这些情况下不指定mem_fn时总是抱怨吗?我总是收到警告C4180(应用于函数类型的限定符没有意义;被忽略)

提前感谢您提供的任何见解。

我对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)());
...
。。。
向量va;
va.推回(新A);
对于每个(va.begin()、va.end()、bind(mem_-fn(&A::f)、arg1));
对于每个(va.begin(),va.end(),(arg1->*&A::f)();
...
您将得到两次A。在示例中,我只找到带有指针的示例,因此我猜您只能使用带指针的phoenix->*运算符。这应该可以,因为运算符->*绑定到指针。
根据5.5中的规范:

二进制运算符->*绑定其第二个操作数,该操作数应为 键入“指向T的成员的指针”(其中T是一个完全定义的类 类型)指向其第一个操作数,该操作数应为“指向T的指针”类型,或 指向一个类的指针,该类的T是一个明确且可访问的基 阶级

鉴于:

很明显这很难看

  • 使用而不是
    运算符->*
    ,因为它与引用和指针同样有效,无需获取
    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)()));
    }
    
    intmain()
    {
    使用boost::phoenix::bind;
    使用boost::phoenix::arg_name::arg1;
    std::向量vb(1);
    std::for_each(vb.begin()、vb.end()、bind(&A::f、(&arg1->*&B::A)();
    }
    

  • 是的,确实有效!现在我还知道如何处理值:(&arg1->*&A::f)()并且双绑定可以替换为:(&(&arg1->*&B::A)()->*&A::f)(),我不认为这更清楚了…@Ben:不,这会调用UB,除非你是用VC++编译的,而VC++允许将其作为非标准编译器扩展。我将发布一个完整的答案,并提供更多细节。@ildjarn:破坏一致性程序的非标准行为不是扩展,而是错误。@BenVoigt:它不会破坏一致性程序,它允许不一致的程序工作——因此扩展。警告C4180是良性的——在不是您的代码中忽略它。感谢您的详细解释。看起来我即将再次被VC++的“功能”所困扰。很高兴知道我们的测试编译器集(GCC和ICC)我必须抓住它。回顾我的问题,我实际上认为->*操作符可以作为bind的方便替代品。看来我错了,我想我会继续使用(phoenix)绑定这类东西。我不想担心迭代一组值或指针。+1是非常详细的答案。这帮助我更好地理解phoenix。
    int main()
    {
        using boost::phoenix::arg_names::arg1;
    
        std::vector<A> va(1);
        std::for_each(va.begin(), va.end(), (&arg1->*&A::f)());
    }
    
    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)()])
        );
    }
    
    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)()));
    }