我有一个基类,它有一个有时会被调用的成员函数。通常,此函数有一个指向自身的参数。
class Base {
public:
std::function<bool(Base *, int)> foo;
private:
int x{};
public:
static std::shared_ptr<Base> create() {
return std::make_shared<Base>();
}
Base() = default;
const std::function<bool(Base *, int)> &getFoo() const {
return foo;
}
void setFoo(const std::function<bool(Base *, int)> &foo) {
Base::foo = foo;
}
int getX() const {
return x;
}
void setX(int x) {
Base::x = x;
}
};
但是当我有一个派生类时,我该如何设置这个成员函数呢?虽然基类指针可以指向子类对象,但是我直接传入了派生对象,编译器没有通过。
class Derived : public Base {
public:
static std::shared_ptr<Derived> create() {
return std::make_shared<Derived>();
}
};
int main() {
auto d = Derived::create();
d->setX(77);
d->setFoo([](Derived *derived, int x) -> bool { return derived->getX() > x; });
if (d->getFoo()) {
auto res = d->foo(d.get(), 99);
std::cout << res << std::endl;
}
return 0;
}
error: no viable conversion from '(lambda at main.cpp:62:15)' to 'const std::function' b->setFoo([](Derived *derived, int x) -> bool { return derived->getX() > x; }); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
那么,有没有什么好主意将闭包传递给基类,基类调用它而不是派生类,最重要的是闭包有一个参数指向谁传递了闭包!
最佳答案
注意事项
我假设由于某种原因,所讨论的闭包需要访问 Derived
的方法/数据成员,而 OP 的示例并没有很好地表达这一点。否则,为什么不直接使用 Base *
作为输入参数:
b->setFoo([](Base *derived, int x) -> bool { return derived->getX() > x; });
@user3655463 的回答包含了这个案例的完整代码。
简单的解决方案
如果@Yuki 提出的 CRTP 解决方案不适合你,你可以只使用 Base *
作为闭包的参数,并在闭包中使用 static_cast
正文(编译器可以优化转换),像这样:
int main() {
auto d = Derived::create();
d->setX(77);
d->setFoo([](Base *derived, int x) -> bool {
return static_cast<Derived *>(derived)->getX() > x;
});
if (d->getFoo()) {
auto res = d->foo(d.get(), 99);
std::cout << res << std::endl;
}
return 0;
}
如果你真的需要闭包中的类型是Derived *
如果在闭包中使用 Base *
是 Not Acceptable ,您可以使用 Base
中的特殊实现隐藏 setFoo
方法 Derived
将为您进行转换:
class Derived : public Base {
public:
static std::shared_ptr<Derived> create() {
return std::make_shared<Derived>();
}
template <typename Closure>
void setFoo(Closure foo) {
Base::setFoo([foo](Base *base, int x) {
return foo(static_cast<Derived *>(base), x);
});
}
};
int main() {
auto d = Derived::create();
d->setX(77);
d->setFoo([](Derived *derived, int x) -> bool {
return derived->getX() > x;
});
if (d->getFoo()) {
auto res = d->foo(d.get(), 99);
std::cout << res << std::endl;
}
return 0;
}
这允许您使用与原始主函数中相同的界面。
如果你有很多派生类,又不想在每个类中一遍又一遍地隐藏那个方法
现在事情变得有点复杂,请注意,在您的情况下,这样做很有可能会过度设计,但我只是想证明它可以完成 - 这就是 CRTP 发挥作用的地方。它用于实现一个 mixin,该 mixin 提供了 setFoo
方法的实现:
template <typename ConcreteDerived, typename DirectBase>
class EnableSetFooAndInherit : public DirectBase {
public:
template <typename Closure>
void setFoo(Closure foo) {
DirectBase::setFoo([foo](DirectBase *base, int x) {
return foo(static_cast<ConcreteDerived *>(base), x);
});
}
};
class Derived : public EnableSetFooAndInherit<Derived, Base> {
public:
static std::shared_ptr<Derived> create() {
return std::make_shared<Derived>();
}
};
class Derived2 : public EnableSetFooAndInherit<Derived2, Base> {
public:
static std::shared_ptr<Derived2> create() {
return std::make_shared<Derived2>();
}
};
int main() {
auto d = Derived::create();
d->setX(77);
d->setFoo([](Derived *derived, int x) -> bool {
return derived->getX() > x;
});
if (d->getFoo()) {
auto res = d->foo(d.get(), 99);
std::cout << res << std::endl;
}
auto d2 = Derived2::create();
d2->setX(77);
d2->setFoo([](Derived2 *derived, int x) -> bool {
return derived->getX() < x;
});
if (d2->getFoo()) {
auto res = d2->foo(d.get(), 99);
std::cout << res << std::endl;
}
return 0;
}
关于c++ - c++中基类如何调用派生类传递的闭包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50334298/