【问题标题】:Is it possible to cast from vector parent* to vector child* in a function without using templates是否可以在不使用模板的情况下在函数中从向量父 * 转换为向量子 *
【发布时间】:2018-10-07 23:54:45
【问题描述】:

在开发数据导入系统时,我决定将大量派生自一个类的对象存储在指向父类的指针向量中。然后我想要一个函数,它返回任何类型的子指针的向量(使用让我知道它是什么类型的子指针的参数)。

我在这里设法实现了类似的简化代码,但它使用模板和强制转换,我觉得只有强制转换就足够了。但是编译器不想从向量 A* 到向量 B* 进行任何转换。

编辑:在实际代码中,有很多子类,而不仅仅是 B,所以用 B 替换模板不是一种选择,抱歉不够精确。

#include <vector>

using namespace std;

class A
{
  public:

    int attr;

    A(): attr(1) {}
};

class B : public A
{
  public:

    B(): A() {attr = 2;}
};

template <typename O>
vector<O*> bees(vector<A*> vecA)
{
    auto vecO = vector<O*>();

    for (auto it = vecA.begin(); it != vecA.end(); it++)
    {
        if ((*it)->attr == 2)
        {
            vecO.push_back(reinterpret_cast<O*>(*it));
        }
    }

    return vecO;
}

int main()
{
    auto vecA = vector<A*>();

    vecA.push_back(new A());
    vecA.push_back(new B());
    vecA.push_back(new B());

    vector<B*> vecB = bees<B>(vecA);
}

所以我的问题是:是否可以在不使用模板的情况下让代码产生相同的效果?如果不是,编译器会用这个生成特定的代码吗?知道无论模板如何,理论上运行时都没有区别。

谢谢。

【问题讨论】:

  • 你为什么使用reinterpret_cast
  • 我正在使用 reinterpret cast 因为它我不强制转换它只是不编译,而且我觉得它在这里是最合适的,因为我知道我不需要对对象进行任何更改.
  • 这味道。 std::vector 的原始指针是合理的,但在这里不是。您拥有 UB,因为您将指向 A 的指针转换为指向 B 的指针。而且您在调用bees 时不必要地复制vecA,因为您不使用引用。这强烈地感觉你有一个 XY 问题。
  • 通用基类的一般概念是它定义了一个所有派生类都遵循的接口。每当您觉得需要存储派生类的类型 (attr) 以便您可以区别对待它们 时,这表明它们 具有相同的接口.那么他们也不应该有一个共同的基类。你只是看到了这个问题的一个症状,“修复”这部分是治疗症状而不是疾病。
  • 如果基类是多态的(即至少有一个虚成员函数),您可以使用dynamic_cast 来检查A* 是否实际上指向O,并将结果推送到向量。如果您希望您的代码适用于从A 派生的任何类,它要么需要是模板,要么需要为每个派生类显式重载。调用者还需要提供信息,以便编译器可以计算出要转换为的类型 - 使用重载函数,一种方法是提供一个额外的参数来提供类型信息。

标签: c++ templates pointers vector reinterpret-cast


【解决方案1】:

您应该考虑在层次结构中移动类型检查(这使用模板,但没有强制转换):

class A {
public:
    virtual ~A(); // you _are_ in a hierarchy of objects

    template<typename T>
    virtual T* as() const { return nullptr; }
};

template<typename X>
class Convertible: public A {

    template<typename T>
    virtual T* as() const {
        if constexpr(std::is_same_v<X, T>)
            return this;
        else
            return nullptr;
    }
};

class B: public Convertible<B> {
};

template <typename O>
vector<O*> bees(vector<A*> vecA)
{
    auto vecO = vector<O*>();

    foreach (auto ptr: vecA)
    {
        auto ptrO = ptr->as<O>();
        if (ptrO)
            vecO.push_back(ptrO);
    }
    return vecO;
}

几点:

OP 的评论:

我正在使用重新解释强制转换,因为我不强制转换它只是无法编译,而且我觉得这是最合适的,因为我知道我不需要对对象进行任何更改。

dynamic_cast 通常是类层次结构设计不足的症状。每当您认为“我可以使用 dynamic_cast 解决这个问题”时,请考虑将代码添加到您的类层次结构中。

【讨论】:

    【解决方案2】:

    继承和多态应该隐藏不同的子类型,因此您的其余代码不必担心特定类型。

    将对象转换为特定类型很可能不是您尝试做的任何事情的正确方法。让类型决定它们的使用方式和作用,而不是外部世界。

    如果您想过滤向量以仅获取具有特定属性的对象,则不应查看它们的类型,而应询问它们是否具有您要查找的属性:

    void FilterA(const std::vector<A>& source, std::vector<A>& destination, std::function<bool(const A&)> filter) {
        std::copy_if(source.begin(), source.end(), std::back_inserter(destination), filter);
    }
    

    那么你可以这样称呼它:

    std::vector<A> filteredVecA;
    FilterA(vecA, filteredVecA, [](const A& a){return a.HasSomePropertyYouCareAbout();});
    

    【讨论】:

      【解决方案3】:

      由于您想要一个可以返回任何类型子指针向量的函数,所以我认为需要模板来指定子类型,但不需要像 reinterpret_cast 等某些东西,这里是示例实现:

      class A
      {
        public:
      
          int attr;
      
          A(): attr(1) {}
          virtual ~A() {};
      };
      
      class B : public A
      {
        public:
      
          B(): A() {attr = 2;}
      
      };
      template<typename T>
      vector<T*> bees(const vector<A*> &vecA)
      {
          vector<T*> vec;
      
          for (auto it = vecA.begin(); it != vecA.end(); it++)
          {
              T* ptr = dynamic_cast<T*>(*it);
              if(ptr != nullptr)
              {
                vec.push_back(*it);
              }
          }
      
          return vec;
      }
      

      我们使用 dynamic_cast 是因为我们将父类型向下转换为子类型。此外,为了使 dynamic_cast 工作,我们需要虚拟析构函数/虚拟函数,因为它需要 RTTI

      【讨论】:

      • 虽然我怀疑 OP 提出了正确的问题,但我也怀疑这是对他或她问题的回答。问题是不使用使用模板函数找到解决方案。
      • @TobiMcNamobi 但是在某种程度上你必须指定你想要创建的子类型向量。还有其他方法也可以像使用 if/else 然后基于它的类型转换但是模板看起来更优雅
      • 您的代码至少可以说是朝着正确方向迈出的一步。而且,是的,我喜欢模板。这不是 OP 要求的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多