代码之家  ›  专栏  ›  技术社区  ›  PinkTurtle

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

c++
  •  0
  • PinkTurtle  · 技术社区  · 7 年前

    我怎么能 显示

    我想做点像 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;
    }
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   eerorika    7 年前

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

    现在可以插入 包装纸 实例转换为字符流:

    for (const shared_ptr<Wrapper>& w : wrapped) {
            std::cout << *w;
    }
    
        2
  •  1
  •   Peter    7 年前

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

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

    Wrapper ,该实现没有关于从中派生哪些类的信息。单独的编译模型(允许独立编译源文件并将其链接在一起)意味着编译器无法查看在不同编译单元中定义的派生类。

    "XY problem" ,其中您尝试执行“X”,认为“Y”将是一个解决方案,因此您询问了如何执行“Y”-在您的情况下,这是一个没有解决方案的问题,阻止人们帮助您。

    将来,当问问题时,试着恰当地描述你真正的问题(“X”),最好不要描述你心目中的解决方案(“Y”)。

    幸运的是,你是少数询问如何解决“XY问题”的人,并且实际上已经费心在你的问题主体中包括对你真正问题的描述。这意味着有可能提供帮助,我现在就尝试一下。

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

    在您的情况下,实际问题是您的

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

    main() ,该函数没有提供足够的信息使用法正常工作

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

    第一步 包装纸 因此虚函数不会返回 void *

    virtual Wrapper* get1() const = 0;
    

    宣布 operator<<() 这就允许 包装纸

    std::ostream &operator<<(std::ostream &, const Wrapper &);
    

    也可以将此函数声明为的朋友 . 这个 const 表示将对象输出到流不会改变它的正常期望。通过引用传递两个参数( &

    第三步 包装纸 可以用来做输出的;

    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() 因此它返回当前对象的地址。您的实现动态创建了一个克隆,这会导致内存泄漏(除非调用方采取特定步骤释放动态分配的对象)。

    第五步 定义先前声明的 操作员<<() 所以它调用虚函数

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

    w.Output(s) 实际上是一个虚拟函数的调用。所以如果 w 是对 Derived<int> , w、 输出(s) 将正确地调用 Derived<int>::Output() 没有任何必要以某种方式投下 派生<int> .

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

    现在起作用了。机制是这样的 get1() W ). 这个 * 操作员<<() 调用虚函数,并解析(自 W 指向 派生<int> 或者 Derived<float> Output() -因为它是模板类的成员 ,正确访问该成员 m_value .

    你想考虑的额外细节

    1) get2() 圣殿骑士团成员 Derived<T> 如果需要,该函数也可以由 Derived<T>::Output() (但不是由 操作员<<() 自从 没有这样的功能,并且 操作员<<() 对派生类一无所知 ).

    2) 虽然你的声明

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

    现在可以工作了,可以省略所有与 完全地,简单地做

     cout << *w << " ";
    

    要正确输出对象,请执行以下操作: *w 获取(类型)的引用 Wrapper & )指向所指向的对象 W . 也就是说 将正确调用虚拟函数的最派生形式 输出()

    最后说明 我再次强调,没有必要解决您提出的不可能解决的问题(如何自动转换为最派生的类型)。