【问题标题】:rebind one parameter of std::function重新绑定 std::function 的一个参数
【发布时间】:2016-02-19 13:06:40
【问题描述】:

在我的 C++ 项目中,我决定尝试新的 ++ 功能。其中一项功能是通过std::bind 将函数与std::function 绑定。

现在我遇到了一个用例,我必须重新绑定std::function

考虑以下简化代码:

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-&gt;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
}

由于leftModelrightModel 必须是同一类的新模型,我需要重新绑定我之前在自动转译类Test::scene 中提供的第一个参数。

也许我走错了路。我希望其他上下文可以解释我遇到这个问题的原因。

【问题讨论】:

  • third()不带任何参数,为什么要在这里使用bind
  • @tobi303 third() 是一个成员函数,因此第一个参数始终是执行它的Test 对象的指针。
  • @Timm:请提供更多信息,因为不清楚您想要实现什么
  • @SimonKraemer 是的,我知道,但是我可能会简单地调用具有不同对象的方法,而不是 bind 不同对象的方法
  • 即使忽略third 是一个类方法这一事实,我也不明白为什么需要“重新绑定”。只需通过将third 绑定到其他参数来创建另一个function,您不必重用旧函数。

标签: c++ c++11 stdbind


【解决方案1】:

正如@tobi303 和其他人所指出的,second 的参数的函数签名过于受限,因为它需要一个空函数。没有什么可以重新绑定 - 它不带任何参数。

为了实现您想要做的事情,您需要减少second 的参数限制。有几种方法可以做到这一点(使其采用function,或指向成员函数的指针);下面显示了它由一个接受一元参数的函数模板参数化:

#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 肯定,大多数情况下function - 只是在当前的 lambda 函数之前,而且更少并且对它们的需求更少。

【讨论】:

  • 完全同意 lambda 在大多数情况下比std::function 更自然。
【解决方案2】:

我该如何处理fun-&gt;rebindLastParameter(other); 部分(将this 指针替换为other)?

一般来说,你不能。

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

如果您确定fun 肯定存储了bind(&amp;Test::third, this) 返回的对象,则无需重新绑定参数,只需修改fun 以使用正确的参数保存一个完全不同的函数对象,即

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

如果您知道它肯定存储了一个由bind( &amp;Test::???, other) 返回的对象,其中&amp;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】:

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

    【讨论】:

    • 谢谢,我现在使用 std::mem_fn 并默认传递 this 并在我的特殊用例中传递 other
    猜你喜欢
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    • 2013-12-14
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多