在C ++中自动向下转换函数参数

时间:2018-07-10 00:49:15

标签: c++ inheritance casting

我有两个类,比如说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个类(一个超类和2个子类)。不幸的是,这使最简单,最干净的解决方案不合适,因为在我看来(如果我错了,请纠正我),它们的伸缩性很差。

2 个答案:

答案 0 :(得分:3)

我可以根据您的需要考虑三种可能的解决方案。在示例中,我用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::variantstd::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适当的重载。

答案 1 :(得分:1)

通常的方法不是向下转换,而是使用虚函数。即将void foo()放在课程的 中。

#include<iostream>
class Base {
public:
    virtual ~Base() = default;
    virtual void foo() { std::cout << "Base foo()\n"; }
};
class Derived : public Base {
public:
    void foo() override { std::cout << "Derived foo()\n"; }
};

int main()
{
    Base* b = new Derived();
    b->foo();
    delete b;
}

输出:

Derived foo()

如果您不想拨打Base::foo(),可以设置

class Base {
public:
    virtual ~Base() = default;
    virtual void foo() = 0;
};

使Base成为抽象类。

但是如果您真的要调用foo(b),则可以使用(模板化)帮助函数。例如:

#include<iostream>
class Base {
public:
    virtual ~Base() = default;
    virtual void foo() = 0;
};
class Derived : public Base {
public:
    void foo() override {
        std::cout << "Derived foo()\n";
    }
};

template<typename T>
void foo(T* t)
{
    t->foo();
}

int main()
{
    Base* b = new Derived();
    foo(b);
    delete b;
}