C ++ std :: array cast

时间:2017-05-03 14:24:16

标签: c++ arrays c++11

我有4个课程:FoundationFoundationWidgetGameGameWidgetFoundationWidget扩展FoundationGameWidget扩展Game

Game还包含Foundation的std ::数组,GameWidget包含FoundationWidget的std ::数组。 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都没有帮助。

我在这里缺少什么?有没有办法投射数组?如果没有,我可以使用一些结构来获得相同的结果,即“虚拟”类成员吗?

2 个答案:

答案 0 :(得分: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 :(得分:1)

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

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

也无法可靠地投射此类物体的数组。

有几种方法可以达到你想要的效果,有些方法比其他方式更多。

  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(这可能太长,无法在此处显示)。