通过将父类强制转换为对子类的引用来提供类的可迭代"views"

Providing iterable "views" of a class by casting parent class to reference to child

本文关键字:引用 views 迭代 子类 父类 转换      更新时间:2023-10-16

我有一个类,有几种方法可以对它进行迭代。例如,您可以迭代查看它内部的所有ABC。该类没有虚拟函数。

我希望这些有一个好的界面。我想知道以下是否给出了未定义或未指定的行为,或者是否可以这样做。

我正在考虑制作这个类的三个派生类:

// can look at this in three ways
struct myClass {
    void myFunc();
};
struct A_view : public myClass {
    A_iterator begin();
};
struct B_view : public myClass {
    B_iterator begin();
};

struct C_view : public myClass {
    C_iterator begin();
};

然后

A_view& get_C_view(const myClass& c) {
    return *reinterpret_cast<B_view*>(&c);
}
B_view& get_B_view(const myClass& c) {
    return *reinterpret_cast<B_view*>(&c);
}
C_view& get_C_view(const myClass& c) {
    return *static_cast<C_view*>(&c);
}

我希望能够像一样使用它

myClass inst;
for (auto& c : get_C_view(inst))
    //stuff
for (auto& b : get_B_view(inst))
    //stuff

auto& bview  = get_b_view(inst);
std::transform(bview.begin(), bview.end(), bview.begin(), [](auto& x, auto& y) { /* smthing */ });
bview.myFunc();

或者使用来自CCD_ 4的算法。正如您所看到的,您还可以在视图上使用父类中定义的函数。

因此,我将一个实例强制转换为对从其派生的类型的引用,该类型只添加函数,而不添加数据,但它实际上不是这些类型之一。

这样可以吗?我所说的"好"是指

  1. 这种行为是定义明确的、未定义的还是未指定的
  2. 如果没有很好地定义,这会在实践中引起问题吗
  3. 赫伯·萨特会皱眉头吗

在这种情况下,继承不是正确的工具,原因有多种,第一种是不能将对象强制转换为非类型,但更普遍的是,因为继承是语言中第二高的耦合关系,应该谨慎使用(即,在需要时,而不仅仅是因为)。

您可以创建包含对您的类型的引用的瘦包装器类型,并将它们的开始/结束函数映射到组件上的适当视图:

struct C_view {
   MyClass &obj;
   C_view(MyClass& obj) : obj(obj) {}
   C_iterator begin() {
     return obj.c_begin();
   }
   C_iterator end() {
     return obj.c_end();
   }
};

然后用户代码变为:

for (auto &c : C_view(inst)) {
   ...
}