代码之家  ›  专栏  ›  技术社区  ›  Timm

重新绑定std::函数的一个参数

  •  1
  • Timm  · 技术社区  · 9 年前

    在我的C++项目中,我决定尝试一下新的++特性。其中一个特性是将函数与 std::function 通过 std::bind .

    现在我来到了一个用例,我必须 重新绑定 这个 std::函数 .

    考虑以下简化代码:

    class Test
    {
    public:
        void first()
        {
            second(bind(&Test::third, this));
        }
    
        void second(function<void()> fun)
        {
            Test *other = new Test();
            fun->rebindLastParameter(other); // How can I do this?
            fun();
        }
    
        void third()
        {
            // This is called in context of "other"
        }
    }
    

    我如何才能 fun->rebindLastParameter(other); 零件(更换 this 指针 other )?

    (编辑)上下文:

    在我的应用程序中,有几个类继承自一个名为 BaseModel 这些类是从自制的描述语言自动转换而来的。以下类别代表一个简单的“小行星”,由另外两个“小行星“组成:

    #pragma once
    
    #include "BaseModel.h"
    #include <functional>
    
    using namespace std;
    using namespace std::placeholders;
    
    class Test : public BaseModel
    {
    public:
      Test(const OBB& start_obb) : BaseModel(start_obb) {}
    
      void __init() {
        scene();
      }
      void scene() {
          combine(bind(&Test::asteroid, this),bind(&Test::asteroid, this));
      }
    
      void asteroid() {
          translate(random_float(),random_float(),random_float());
          sphere(7);
          repeat(400,bind(&Test::impact, this));
      }
    
      void impact() {
          auto p1 = random_surface_point();
          select_sphere(p1,random_float() * 2 + 1,1.0);
          normalize(p1);
          translate_selection(-p1 * random_float() * 0.4);
      }
    
    };
    

    问题在于功能 BaseModel::combine ,它(通过构造立体几何)结合了两个新天体(第一小行星和第二小行星):

    void BaseModel::combine(function<void()> left, function<void()> right)
    {
        BaseModel *leftModel = (BaseModel*) ::operator new (sizeof(BaseModel));
        BaseModel *rightModel = (BaseModel*) ::operator new (sizeof(BaseModel));
        leftModel->initWithStartOBB(*this->obb);
        rightModel->initWithStartOBB(*this->obb);
    
        auto newLeft = bind(left, leftModel); // This does not work
        auto newRight = bind(right, rightModel);  // This does not work
    
        newLeft();
        newRight();
    
       // ... CSG stuff
    }
    

    leftModel rightModel 必须是同级别的新车型,我需要 重新绑定 我之前在自动编译类中给出的第一个参数 Test::scene .

    也许我走错了方向。我希望这个额外的背景可以解释我为什么会遇到这个问题。

    3 回复  |  直到 9 年前
        1
  •  3
  •   Ami Tavory    9 年前

    正如@tobi303和其他人所指出的 second 因为它需要一个nullary函数。没有什么可以重新绑定的,它不需要任何参数。

    为了实现看起来像你想做的事情,你需要有 第二 不那么拘束。有几种方法可以做到这一点 function 或指向成员函数的指针);下面显示了由接受一元参数的函数参数化的it模板:

    #include <functional>                                                                                                                           
    
    
    using namespace std;
    
    
    class Test
    {   
    public:
        void first()
        {   
            second([](Test *p){p->third();});
        }   
    
        template<class Fn> 
        void second(Fn fn) 
        {   
            Test *const other = new Test();
            fn(other);
        }   
    
        void third()
        {   
            // This is called in context of "other"
        }   
    };  
    
    
    int main()
    {   
        Test t;
        t.first();
    }   
    

    个人观点(这一观点给我赢得了几张反对票):我认为使用许多库和技术,大多数情况下 bind 当然,大多数情况下 作用 -只需在当前lambda函数之前,对它们的需求就越来越少。

        2
  •  2
  •   Jonathan Wakely    9 年前

    我如何才能 fun->rebindLastParameter(other); 零件(更换 this 指针 other )?

    一般来说,你不能。

    std::function 依赖于 type-erasure 这意味着存储在 std::函数 不是类型的一部分,并且不容易访问。

    如果你确定 fun 绝对存储由返回的对象 bind(&Test::third, this) 那么无需重新绑定参数,只需修改 享乐 用正确的参数保存完全不同的函数对象,即。

    fun = bind(&Test::third, other);
    

    如果您知道它确实存储了 bind( &Test::???, other) 哪里 &Test::??? 是具有的任何成员函数 确切地 与相同的签名 Test::third 那么你可以这样做:

    using binder_type = decltype(bind(&Test::third, this));
    if (auto target = fun.target<binder_type>())
    {
       // *target is the result of the bind() expression
    }
    

    但仍无法在 *target 更换 Test* 指针。

    如果您知道可以使用的函数集,则可以执行以下操作:

    using binder_foo_type = decltype(bind(&Test::foo, this));
    using binder_bar_type = decltype(bind(&Test::bar, this));
    using binder_baz_type = decltype(bind(&Test::baz, this));
    if (fun.target<binder_foo_type>())
    {
       fun = std::bind(&Test::foo, other);
    }
    else if (fun.target<binder_bar_type>())
    {
       fun = std::bind(&Test::bar, other);
    }
    else if (fun.target<binder_baz_type>())
    {
       fun = std::bind(&Test::baz, other);
    }
    

    但这些都不是通用的解决方案,也不太容易维护。

        3
  •  1
  •   hlscalon    9 年前

    std::mem_fn 这是您正在寻找的,因为它为指向成员的指针生成包装对象。

    std::function<void(Test*)> f = std::mem_fn(&Test::third);
    f(this);
    Test *other = new Test();
    f(other);
    

    具有 std::bind ,将fun用于 Test 对象这样,您可以使用新对象调用它

    void second(std::function<void(const Test&)> fun)
    {
        Test *other = new Test();
        fun(*other);
    }
    

    或与 this 对象

    fun(*this);