基类如何调用c ++中派生类传递的闭包?

时间:2018-05-14 15:45:45

标签: c++ c++11

我有一个基类,它有一个成员函数,有时会被调用。通常,此函数有一个指向自身的参数。

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;});                 ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~

那么,有没有什么好主意将闭包传递给基类,而基类调用它而不是派生类,最重要的是闭包有一个参数,指向谁传递闭包!

3 个答案:

答案 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;
}

Live example

如果您确实需要闭包中的类型为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;
}

这使您可以使用与原始主功能相同的界面。

Live example

如果您有很多派生类,并且不希望在每个类中反复隐藏该方法

现在事情变得有点复杂了,请注意,在你的情况下,做这样的事情很有可能是过度工程,但我只是想证明它可以做到 - 这就是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;
}

Live example

答案 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;
}