我有两个类,比如说Base和Derived:

class Base {
public:
    virtual ~Base() = 0;
};
class Derived : public Base {};

还有一个函数foo:
auto foo (Derived* d) {
    ...
}

是否可以自动下调其参数?所以我可以做这样的事情:
Base* b = new Derived();
foo(b);

基本上,我想编写此函数而不在函数调用之前显式转换它。
我阅读了一些有关转换运算符/构造函数的信息,但在我看来它们没有用,您还有其他想法吗?

编辑:对不起,我用2个类和一个函数简化了这个问题。但是实际上我有一个包含50个函数的函数库和3个类(一个父类(super class)和2个子类)。不幸的是,这使最简单,最干净的解决方案不合适,因为在我看来(如果我错了,请纠正我),它们的伸缩性很差。

最佳答案

我可以根据您的需要考虑三种可能的解决方案。在示例中,我已经用unique_ptr替换了原始指针。

情况1:您不需要每个派生类型的基本类型都相同。

使用CRTP允许基本类型将自身作为派生类型调用。示例实现:

template <typename DerivedType>
class Base {
    template <typename F>
    auto invoke_as_derived(F&& f) {
        return std::forward<F>(f)(static_cast<DerivedType*>(this));
    }
};
class Derived : public Base<DerivedType> {};

用法:
std::unique_ptr<Base<Derived>> b = std::make_unique<Derived>();
b->invoke_as_derived(foo);

由于您提到使用基本指针列表,因此这可能对您不起作用。

情况2:您需要共享的基本类型,但类型层次结构中只有一层,没有虚拟方法。

使用 std::variant std::visit
class Derived {};
using Base = std::variant<Derived, /* other derived types */>;

auto foo(Derived*) { ... }

class FooCaller {
    operator ()(Derived& d) {
        return foo(&d);
    }
    // Overload for each derived type.
}

用法:
Base b = Derived();
std::visit(FooCaller{}, b);

情况3:您需要一个基本类型,但还需要虚拟方法和/或类型层次结构中的其他层。

您可以尝试visitor pattern。它需要一些样板,但根据您的需要,它可能是最佳解决方案。执行示意图:
class Visitor; // Forward declare visitor.

class Base
{
public:
    virtual void accept(Visitor& v) = 0;
};
class Derived : public Base
{
public:
    void accept(Visitor& v) final { v.visit(*this); }
};

struct Visitor
{
    virtual void visit(Derived&) = 0;
    // One visit method per derived type...
};
struct FooCaller : public Visitor
{
    // Store return value of call to foo in a class member.
    decltype(foo(new Derived())) return_value;

    virtual void visit(Derived& d)
    {
        return_value = foo(&d);
    }
    // Override other methods...
};

用法:
std::unique_ptr<Base> b = std::make_unique<Derived>();
FooCaller foo_caller;
b->accept(foo_caller);

您可以编写一个访问者,该访问者需要将一个函数应用于该元素,因此不必对所有许多函数都重复此操作。另外,如果您可以自己更改功能,则可以用访问者类型替换功能。

编辑:将调用语法简化回foo(b)
为要传递Base对象的每个函数重载集定义一个重载。示例,使用第三种技术:
auto foo(Base* b) {
    FooCaller foo_caller;
    b->accept(foo_caller);
    return std::move(foo_caller.return_value);
}

现在foo(b.get())将在运行时委派给适当的foo重载。

关于c++ - 在C++中自动向下转换函数参数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51255820/

10-14 15:18
查看更多