重新绑定 std::function 的一个参数
rebind one parameter of std::function
在我的C++项目中,我决定尝试一下new++功能。其中一个功能是通过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->rebindLastParameter(other);
部分(将指针替换为other
this
)?
(编辑)上下文:
在我的应用程序中,有几个类继承自一个名为 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
,它结合了(通过构造立体几何)两个新天体(第1颗小行星和第2颗小行星):
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
中给出的第一个参数。
也许我走错了路。我希望额外的上下文可以解释为什么我会遇到这个问题。
正如@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函数之前,并且越来越不需要它们。
如何执行
fun->rebindLastParameter(other);
部分(将指针替换为other
this
)?
一般来说,你不能。
std::function
依赖于类型擦除,这意味着存储在std::function
中的对象的原始类型不是类型的一部分,并且不容易访问。
如果您确定fun
肯定存储了bind(&Test::third, this)
返回的对象,则无需重新绑定参数,只需修改fun
以使用正确的参数保存完全不同的函数对象,即
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);
}
但这些不是通用解决方案,也不是很易于维护。
>std::mem_fn
是您要查找的内容,因为它为指向成员的指针生成包装器对象。
std::function<void(Test*)> f = std::mem_fn(&Test::third);
f(this);
Test *other = new Test();
f(other);
使用std::bind
,使用有趣的引用Test
对象。这样,您可以使用新对象调用它
void second(std::function<void(const Test&)> fun)
{
Test *other = new Test();
fun(*other);
}
或者使用this
对象
fun(*this);
- 构造函数在退出函数时无法初始化一个参数
- 运算符重载:"operator+"必须采用零个或一个参数
- 如果需要转换,我可以在读取参数的同时将其移动到另一个参数吗?
- 两个函数模板候选项.将一个参数作为引用后,选择不太专业的模板
- 如何设置默认参数以防用户不输入另一个参数
- 使用一个参数的模板函数时出错(适用于 2)
- 仅当一个参数中未使用 std::function 时,模板函数替换才有效
- 如何在两个树通用的函数中创建一个参数?
- 使用带有一个参数函数的递归找到数字的平方
- 专门处理一个参数(C++模板)的两个模板参数
- 模板函数,其中一个参数需要专门化,而另一个不需要
- 如何更改参数包中的最后一个参数
- C++ 如何使用一个参数从派生类构造函数中调用具有两个参数的超类构造函数
- 模板参数重载,最后一个参数为非类型名
- 根据一个参数对数组进行排序
- 是否可以将指针传递给运算符,作为一个参数,例如指向函数的指针
- 用一个参数计数圆形链接列表中的节点
- 将数组作为一个参数从一个函数传递到C 中的另一个函数
- 将另一个参数传递给函子的库
- 分叉:使用一个参数运行 shell 时资源暂时不可用