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

标签 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;
}

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

Live example .

如果你真的需要闭包中的类型是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;
}

这允许您使用与原始主函数中相同的界面。

Live example .

如果你有很多派生类,又不想在每个类中一遍又一遍地隐藏那个方法

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

Live example .

关于c++ - c++中基类如何调用派生类传递的闭包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50334298/

相关文章:

C++ 打破循环包含

c++ - 哈希和模运算

c++ - ostream插入运算符与其非成员重载的关系

c++ - 将类成员函数作为同一类的另一个成员函数的参数传递

linux - C++中两个日期时间之间的区别

c++ - 为什么 iostream 不可复制?

C++-错误C2144语法错误: 'int' should be preceded by ';'

从基础到派生的 shared_ptr 的 C++ dynamic_ptr_cast 失败

c++ - 使用私有(private)复制/移动构造函数进行聚合初始化

c++ - 如何修复 istringstream 编译器错误?