我有一个基类,它有一个成员函数,有时会被调用。通常,此函数有一个指向自身的参数。
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;
}
错误:没有可行的转换'(lambda at main.cpp:62:15)'到'const 的std ::函数” b-&gt; setFoo([](Derived * derived,int x) - &gt; bool {return derived-&gt; getX()&gt; x;}); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
那么,有没有什么好主意将闭包传递给基类,而基类调用它而不是派生类,最重要的是闭包有一个参数,指向谁传递闭包!
答案 0 :(得分:6)
我将假设由于某种原因,有问题的闭包需要访问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 *
不可接受,您可以使用setFoo
中的特殊实现隐藏Base
方法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,它提供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;
}
答案 1 :(得分:2)
如果模板基础解决方案符合您的风格,那么这可能会有效。
template <typename D>
class Base {
public:
std::function<bool(D*, int)> foo;
private:
int x{};
public:
static std::shared_ptr<Base> create() { return std::make_shared<Base>(); }
Base() = default;
const std::function<bool(D*, int)>& getFoo() const { return foo; }
void setFoo(const std::function<bool(D*, int)>& foo) { Base::foo = foo; }
int getX() const { return x; }
void setX(int x) { Base::x = x; }
};
class Derived : public Base<Derived> {
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;
}
答案 2 :(得分:2)
你不能只使用Base(正如你设计的那样):
d->setFoo([](Base* derived, int x) -> bool { return derived->getX() > x; });
整个代码:
#include <algorithm>
#include <iostream>
#include <vector>
#include <functional>
#include <memory>
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([](Base* 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;
}