2017-05-03 97 views
0

我有4个类:Foundation,FoundationWidget,GameGameWidgetFoundationWidget延伸FoundationGameWidget延伸GameC++ std :: array cast

Game还包含Foundation秒的标准::阵列和GameWidget包含FoundationWidget秒的标准::阵列。 Game也包含一个虚拟方法,它应该返回指向数组Foundation s的指针。 GameWidget通过返回指向其FoundationWidget的数组的指针覆盖此方法。 目前(非工作)的实现看起来是这样的:

class Game { 
    std::array<Foundation, 4> foundations; 
private: 
    virtual std::array<Foundation, 4>* get_foundations() { 
     return &this->foundations; 
    } 
} 

class GameWidget : public Game { 
    std::array<FoundationWidget, 4> foundations; 
private: 
    std::array<Foundation, 4>* get_foundations() { 
     return &this->foundations; 
    } 
} 

我期望这个工作,因为它是与类相同大小的阵列,扩展指定为返回类型的人,而是我收到这个错误:cannot convert ‘std::array<FoundationWidget, 4ul>*’ to ‘std::array<Foundation, 4ul>*’ in return

我也试着将类属性声明为指针数组,但结果是一样的。 static_cast或dynamic_cast都没有帮助。

我在这里错过了什么?有没有什么方法来投射数组?如果不是,我可以使用一些建设来获得相同的结果,即“虚拟”类成员吗?

+0

如果'FoundationWidget'从'Foundation'继承为什么不只是有一个'的std ::阵列<基金会*,4>'(或'其他一些指针类型GameWidget'? – NathanOliver

+0

也,你回来'STD :: array *'这是一个指针类型,如果你正在考虑类似getter的东西,你应该小心不要返回一个临时/局部变量的指针 –

+0

@NathanOliver谢谢你的评论。已经解决了这个问题,但是我仍然对这个演员感兴趣 – sveatlo

回答

2

数组是打包某些类型数据的值。不同类型的数组与数组不兼容。例如,它们的大小可能不同。并且数组被打包,因此大小为7的元素数组和大小为8的元素数组将不起作用。

即使它们的大小相同,C++也会规定这些数组是不可转换的。

您可以通过array<unique_ptr<Base>, N> - (智能)指针数组来解决此问题。如果你想避免额外的碎片和分配,你也可以写一个类型擦除类型any_derived<Base, size_limit>类型并有一个array<any_derived<Base, size_limit>, N>并连续存储它们。

但实际上只是使用unique_ptr

using foundations_t = std::array<std::unique_ptr<Foundation>, 4>; 
template<class T> struct tag_t {}; 
struct HasFoundation { 
    foundations_t foundations; 
    HasFoundation(tag_t<T>) { 
    std::generate(foundations.begin(), foundations.end(), []{return std::make_unique<T>(); }); 
    } 
    HasFoundation(HasFoundation const&)=delete; 
    HasFoundation(HasFoundation&&)=default; 
    HasFoundation& operator=(HasFoundation const&)=delete; 
    HasFoundation& operator=(HasFoundation&&)=default; 
}; 
class Game:HasFoundation { 
protected: 
    template<class T> 
    Game(tag_t<T> tag):HasFoundation(tag){} 
public: 
    Game():Game(tag_t<Foundation>{}) {} 
    virtual foundations_t* get_foundations() { 
    return &this->foundations; 
    } 
}; 

class GameWidget : public Game { 
public: 
    GameWidget():Game(tag_t<FoundationWidget>{}) {} 
}; 

这里我们有一个存储类,它存储的是在施工时确定的。

模板版本将要求在头文件中公开Game中的所有逻辑。这个需要所有对“真实”类型的foundations元素的访问都需要运行时调度。

+1

请注意,使用'array ,4>',数组初始化为空'unique_ptr's,而OP的值数组具有默认构造的'Foundation'和'FoundationWidget's。 'generate(foundations.begin(),foundations.end(),make_unique );'call将初始化Foundations – Caleth

+0

@Caleth生成不够好,因为你不能复制唯一的指针。我想你错过了一个lambda。我的意思是,我们都不是? – Yakk

+0

一个lambda将definatly工作,但iirc生成不会复制,它分配http://en.cppreference.com/w/cpp/algorithm/generate“该类型的Ret必须是这样的,一个ForwardIt类型的对象可以被解除引用并分配了类型Ret的值。“你在想'std :: fill'吗? – Caleth

1

A GameWidget对象包含一个4 FoundationWidget对象的数组,也包含一个不同数组的对象(间接地通过其基类子对象)。如果这是你想要的,那很好。我以某种方式怀疑它,并假设你确实想要别的东西,尽管遏制问题与返回类型get_foundations()的问题无关。

无论哪个对象包含哪个数组,这两个数组类型都不会形成协变返回类型。只有通过继承相关的类以及指向这些类的指针/引用可能会形成协变返回类型。std::array这样的类和指向这样的数组和数组的指针of指向这样的类的指针等本身并不相关,不能共同使用。所以你的期望很不幸在现实中没有支持。

也没有办法可靠地投出这样的对象的数组。

有几种方法可以实现你想要的,比其他更多的参与。

  1. 使Game成为模板。

    template <class F> 
    class Game { 
        std::array<F, 4> foundations; 
    private: 
        virtual std::array<F, 4>* get_foundations() { 
         return &this->foundations; 
        } 
    }; 
    
    class GameWidget : public Game<FoundationWidget> { 
        // nothing here! 
    }; 
    
  2. 不要暴露数组。

    class Game { 
        virtual Foundation* get_foundation (int) = 0; 
    }; 
    
    class GameWidget : public Game { 
        FoundationWidget* get_foundation (int i) { 
         return &foundations[i]; 
        } 
        std::array<FoundationWidget, 4> foundations; 
    }; 
    
  3. 创建根基的家庭定制的容器,从而FoundationWidgetArray继承FoundationArray(这可能是太长时间在这里展示)。