我想有一个对象的方法调用的引用。这在c++中可能吗?我应该搜索的专业名称是什么?我们可以提供一些带有预定值的参数吗?
下面的代码突出显示了我想使用的
struct Foo {
void barNoArgs();
void barMultArgs(int, float, bool);
};
Foo f;
auto& refToBarNoArgs = f.barNoArgs;
refToBarNoArgs(); // should call f.barNoArgs()
auto& refToBarMultArgs = f.barMultArgs(?, 3.14f, ?);
refToBarNoArgs(42, true); // should call f.barMultArgs(42, 3.14f, true)
我应该搜索的技术名称是什么?
这称为参数绑定,有时也称为部分函数
但首先,你应该知道你的两个例子是完全相同的问题。方法本质上是带有隐藏的第一个参数this
的函数。
所以refToBarNoArgs
是一个有0个参数的函数,它调用Foo::barNoArgs
时第一个参数是预先填充的。refToBarNoArgs
是一个接受2个参数的函数,调用Foo::barMultArgs
时,第一个和第三个参数是预先填充的,第二个和第四个参数是自己的参数。
这里的关键是你的"参考";不能是c++意义上的引用。引用实际上并不存在(因为没有与引用本身相关联的对象)。在这两种情况下,我们都需要存储预填充参数的值,并管理它们的生存期。它必须是一个实际的对象,具有存储空间和生命周期,并且表现得像一个函数。这称为函子。
该语言为我们提供了一点方便的语法糖,以便于同时创建这种函子的类型和单个实例:Lambdas.
auto refToBarNoArgs = [&f](){f.barNoArgs();};
refToBarNoArgs();
auto refToBarMultArgs = [&f](int i, bool b){f.barMultArgs(i, 3.14f, b);};
refToBarNoArgs(42, true);
如果您不熟悉lambda,请注意我示例中的[&f]
。这意味着lambda通过引用捕获f
。因此,它们的生存期(例如,存储在std::function<>
中)不应超过f
的生存期。
您也可以使用std::bind
来有效地获得相同的东西。实际上,最终结果非常接近您的原始代码:
#include <functional>
struct Foo {
void barNoArgs();
void barMultArgs(int, float, bool);
};
int main() {
using namespace std::placeholders; // for _1, _2, _3...
Foo f;
auto refToBarNoArgs = std::bind(&Foo::barNoArgs, &f);
refToBarNoArgs();
auto refToBarMultArgs = std::bind(&Foo::barMultArgs, &f, _1, 3.14f, _2);
refToBarNoArgs(42, true);
}
我个人觉得lambda更清晰。
从技术上讲,您可以获得指向成员函数的指针(成员函数指针),然后为对象调用该函数。
struct Foo {
void barNoArgs();
void barMultArgs(int, float, bool);
};
void foo() {
Foo f;
auto refToBarNoArgs = &Foo::barNoArgs;
(f.*refToBarNoArgs)();
}
固定(绑定)参数来创建一个新的函数,然后你可以用丢失的参数调用这个函数,这有点挑战性。我认为最简单的方法是使用lambda函数创建一个未命名的函数来包装成员并捕获对象。另一种选择是使用std::bind
。
#include <functional> // for std::bind
struct Foo {
void barNoArgs();
void barMultArgs(int, float, bool);
};
void foo() {
using namespace std;
using namespace std::placeholders;
Foo f;
auto b = bind( &Foo::barMultArgs, f, _1, 3.14f, _2);
auto l = [&f](auto const& x, auto const& y) {return f.barMultArgs(x,3.14f,y);};
b(42, true); // should call f.barMultArgs(42, 3.14f, true)
l(42, true); // should call f.barMultArgs(42, 3.14f, true)
}