Примечание
Я собираюсь предположить, что по какой-то причине рассматриваемое закрытие нуждается в доступе к методам / элементам данных Derived
, и пример OP не очень хорошо передает это.Иначе, почему бы просто не использовать Base *
в качестве входного параметра:
b->setFoo([](Base *derived, int x) -> bool { return derived->getX() > x; });
@ user3655463 ответ содержит полный код для этого случая.
Простое решение
В случаеCRTP-решение, предложенное @Yuki, не работает для вас, вы можете просто использовать 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 вступает в игру.Он используется для реализации миксина, который обеспечивает реализацию метода 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;
}
Живой пример .