是否可以自动转换为大多数派生类型?

时间:2019-03-10 03:20:53

标签: c++

我编写了一个内存扫描应用程序,其中的“模式”由任意模板化的“单元”组成。单元类也派生自非模板化父包装器类(因此我可以将单元指针存储在向量中。)。

如何显示不在父类中的模板化单元成员,而无需手动向下转换?

我想做类似auto unitT = most_derived_cast(unitW);

的事情

这是示例代码:

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

class Wrapper {
public:
    virtual void* get1() const = 0;
};

template<typename T>
class Derived : public Wrapper {

    T m_value;

public:
    Derived(T value) : m_value{ value } {}

    void* get1() const override {
        return new T(m_value);
    }

    T get2() const {
        return m_value;
    }
};


int main(int argc, char** argv) {

    vector<shared_ptr<Wrapper>> wrapped;

    for (int i = 0; i < 10; i++) {
        wrapped.emplace_back(new Derived<int>(i));
        wrapped.emplace_back(new Derived<float>(i));
    }

    for (const shared_ptr<Wrapper>& w : wrapped) {

        cout << *w->get1() << " ";                      // bummer; can't dereference from void*

        auto v = w->get2();                             // bummer; w is of type Wrapper

        if (dynamic_pointer_cast<Derived<int>>(w)) {    // bummer; have to manually cast
            cout << *(int*)w->get1() << " ";
            cout << dynamic_pointer_cast<Derived<int>>(w)->get2() << " ";
        }
        else if (dynamic_pointer_cast<Derived<float>>(w)) {
            cout << *(float*)w->get1() << " ";
            cout << dynamic_pointer_cast<Derived<float>>(w)->get2() << " ";
        }
        else {
            cout << "bummer "; // bummer; forgot to implement this...
        }
    }

    system("PAUSE");

    return 0;
}

2 个答案:

答案 0 :(得分:1)

  

C ++是否可以自动转换为大多数派生类型?

不。这不可能。通常,当人们认为自己需要此功能时,他们需要的是虚拟功能。

  

如何显示不包含在父类中的模板化单元成员而无需手动向下转换?

解决方案示例:

添加新的虚拟函数以插入流中:

class Wrapper {
public:
    virtual std::ostream&
    stream_insert(std::ostream& os) const = 0;
// ...
};

使Wrapper可以流化,并委托给虚函数:

std::ostream&
operator<<(std::ostream& os, const Wrapper& w) { 
    return w.stream_insert(os);
}

在派生类中实现该功能:

std::ostream&
stream_insert(std::ostream& os) const override {
    return os << m_value;
}

您现在可以将Wrapper个实例插入字符流:

for (const shared_ptr<Wrapper>& w : wrapped) {
        std::cout << *w;
}

答案 1 :(得分:1)

  

是否可以自动转换为大多数派生类型?

正如eeonika的回答所提到的,不,不是。

一般而言,仅给出Wrapper的声明(甚至定义为Wrapper),实现就没有有关从中派生出哪些类的信息。单独的编译模型(允许源文件被独立编译并链接在一起)意味着编译器不了解在不同编译单元中定义的派生类。

但是,这里的主题实际上是错误的问题。实际上,您已经提供了"XY problem"的演示,其中您尝试执行“ X”,认为“ Y”将是一个解决方案,因此您询问了如何执行“ Y”-您的案件是一个无法解决的问题,这会阻止人们为您提供帮助。

以后,在提出问题时,请尝试正确地描述您的REAL问题(“ X”),最好不要考虑您要考虑的解决方案(“ Y”)。

幸运的是,您中的少数人问如何解决“ XY问题”,并且实际上不愿意在问题的正文中包含对REAL问题的描述。这意味着可以提供帮助,我现在将尝试。

  

如何显示不包含在父类中的模板化单元成员而无需手动向下转换?

再次如eeroniko所述,实际的解决方案是提供合适的虚函数。

以您为例,实际的问题是您的class Wrapper { public: virtual void* get1() const = 0; };

main()

已提供了虚函数,但是在cout << *w->get1() << " "; // bummer; can't dereference from void* 中使用该函数时,该函数没有提供足够的信息来使使用正常工作

Wrapper

要解决此问题,需要执行一些步骤。

第一步,请更改void *,以使虚拟函数不会返回virtual Wrapper* get1() const = 0;

operator<<()

第二步声明一个Wrapper,该std::ostream &operator<<(std::ostream &, const Wrapper &); 可以在不更改该对象的情况下输出到流中。

Wrapper

(可选)可以将此函数声明为const的朋友。 &代表正常的期望,即将对象输出到流不会更改它。通过引用传递两个参数(每个参数上的Wrapper)很重要。

第三步提供virtual void Output(std::ostream &) const; 的另一个虚拟函数,可用于执行输出;

Derived

第四步。在模板类virtual中,覆盖两个template<typename T> class Derived : public Wrapper { T m_value; public: Derived(T value) : m_value{ value } {} Wrapper * get1() const override { return this; // note that this does not create a clone }; virtual void Output(std::ostream &s) const override { s << m_value; }; }; 函数。

get1()

请注意,我已经更改了operator<<(),所以它返回了当前对象的地址。您的实现动态创建了一个克隆,这会导致内存泄漏(除非调用者采取特定步骤来释放动态分配的对象)。

第五步定义先前声明的std::ostream &operator<<(std::ostream &s, const Wrapper &w) { w.Output(s); return s; } ,以便调用虚函数

w.Output(s)

w实际上是对虚函数的调用。因此,如果Derived<int>是对w.Output(s)的引用,则Derived<int>::Output()将正确调用Wrapper,而无需将Derived<int>强制转换为 cout << *w->get1() << " ";

将所有内容放在一起,您会发现该语句

get1()

现在有效。机制是w返回对象的地址(即,它返回*)。 operator<<()获得对指向的对象的引用。然后w调用虚函数,并解析(由于Derived<int>指向Derived<float>Output())到Derived的正确重载-由于它是模板类m_value的成员,因此可以正确访问成员get2()

您可能希望考虑的其他优点

1)尽管我已根据需要省略了模板化Derived<T>的{​​{1}}成员,但该函数也可以由Derived<T>::Output()调用(但不能通过operator<<()因为Wrapper没有这样的功能,并且operator<<()对于从Wrapper派生出什么类一无所知。

2)尽管您的声明

 cout << *w->get1() << " ";

现在可以使用,可以完全省略与get1()相关的所有内容,只需执行

 cout << *w << " ";

正确输出对象,因为*w获得对Wrapper &所指向的对象的引用(类型为w)。这意味着operator<<()将正确调用虚拟函数Output()的最派生形式。

最后的提示:我再次强调,您无需解决您所提出的不可能的问题(如何自动将其转换为最衍生的类型)。