我可以在不查看每个元素的情况下将std :: vector <animal *>转换为std :: vector <dog *>吗?</dog *> </animal *>

时间:2009-07-24 03:36:32

标签: c++ templates vector casting

我有一个基类,有几个类扩展它。我有一些通用的库实用程序,它们创建一个包含指向基类的指针的向量,以便任何子类都可以工作。如何将向量的所有元素强制转换为特定的子类?

// A method is called that assumes that a vector containing
// Dogs casted to Animal is passed.
void myDogCallback(vector<Animal*> &animals) {
    // I want to cast all of the elements of animals to
    // be dogs.
    vector<Dog*> dogs = castAsDogs(animals);
}

我天真的解决方案看起来像这样:

// A method is called that assumes that a vector containing
// Dogs casted to Animal is passed.
void myDogCallback(vector<Animal*> &animals) {
    // I want to cast all of the elements of animals to
    // be dogs.
    vector<Dog*> dogs;
    vector<Animal*>::iterator iter;
    for ( iter = animals.begin(); iter != animals.end(); ++iter ) {
        dogs.push_back(dynamic_cast<Dog*>(*iter));
    }
}

7 个答案:

答案 0 :(得分:11)

您可以使用std::transform。它仍在内部使用for(),但您将获得双字符串实现:

#include <vector>
#include <algorithm>
using namespace std;

struct Animal { virtual ~Animal() {} };
struct Dog : Animal { virtual ~Dog() {} };

template<typename Target>
struct Animal2Target { Target* operator ()( Animal* value ) const { return dynamic_cast<Target*>(value); } };

void myDogCallback(vector<Animal*> &animals) {
{
    vector<Dog*> dogs;
    transform( animals.begin(), animals.end(), dogs.begin(), Animal2Target<Dog>() );
}

答案 1 :(得分:0)

当动物矢量包含其他动物专精时,您编写的代码会将一堆空指针放入您的狗矢量中。

vector<Dog*> dogs;
vector<Animal*>::iterator iter;
Dog* dog;

for( iter = animals.begin(); iter != animals.end(); ++iter )
{
  dog = dynamic_cast<Dog*>(*iter);
  if( dog )
  {
    dogs.push_back( dog );
  }
}

答案 2 :(得分:0)

通常使用dynamic_cast进行向下转换并不是很好。您应该重构代码,这样就不需要使用显式的向下转换。

有关详情,请参阅CPP FAQ lite

UPD 另外,请参阅Stroustrup page(搜索“我为什么不能将矢量指定给矢量?”)

答案 3 :(得分:0)

有两种选择。最简单的方法是使用remove_copy_if之类的东西。我无法解释为什么他们这样称呼它,但它将元素从一个容器复制到另一个满足谓词的容器。这是基本想法(未经测试):

struct IsDog : unary_function < Animal *, bool > {
  bool operator ()(Animal * animal) const {
    return dynamic_cast <Dog*> (animal);
  }
};

void foo (vector<Animal*> animals) {
  vector<Dog*> dogs;
  std::remove_copy_if (animals.begin ()
    , animals.end ()
    , back_inserter (dogs)
    , std::not1 ( IsDog () ) );  // not1 here negates the result of IsDog!


  // dogs now contains only animals that were dogs

}

我想一种查看remove_copy_if的方法是将其视为copy_unless

另一种方法,如果你的代码只围绕迭代器,就是包装vector&lt;的迭代器。动物*&gt;只有从收集中只返回狗的人。这里的关键优势是你仍然只有一个容器,但是当你算法将导航整个动物集合时,你会付出更多的代价。

class dog_iterator  // derive from std::iterator probably with bidirectinoal tag
{
private:
  vector<Animals*>::iterator getNextDogIter (vector<Animals*>::iterator iter) {
    while (iter != m_end) {
      if (0 != dynamic_cast<Dog*> (*iter)) {
        break;
      }
      ++iter;
    }
    return iter;
  }

public:
  dog_iterator (vector<Animals*>::iterator iter, vector<Animals*>::iterator end)
  : m_end (end)
  , m_iter (getNextDogIter (iter))
  {
  }

  // ... all of the usual iterator functions

  dog_iterator & operator++ ()
  {
    // check if m_iter already is at end - otherwise:
    m_iter = getNextDogIter (m_iter + 1);
    return *this;
  }
  // ...
};

这非常粗糙,但我希望它向您展示基本原则。

答案 4 :(得分:0)

如果你说你可以保证每个元素都是狗,那么只需static_cast

void myDogCallback(vector<Animal*> &animals) {

    const vector<Animal*>::size_type numAnimals = animals.size();

    vector<Dog*> dogs;
    dogs.reserve( numAnimals );

    for ( vector<Animal*>::size_type i = 0; i < numAnimals; ++i ) {
        dogs.push_back(static_cast<Dog*>( animals[i] ));
    }
}

我经常总是得到人们的下意识反应,这是不好的,你应该总是使用dynamic_cast,但实际上,如果你可以保证类型,那么它是完全安全的,IMO是一个明智的事情要做。

此外,您的保证意味着新的向量具有相同的大小,因此保留相同的空间以避免在每个push_back中进行任何分配。作为一个替代循环,我使用索引只是因为我总是认为使用索引迭代必须比迭代器更快但这可能是废话:)

答案 5 :(得分:0)

如果可以确定,std::vector<Animal*>仅包含Dog*,则可以使用reinterpret_cast

答案 6 :(得分:0)

std::transform方法与static_cast混合(因为您确定其安全性)可能如下所示:

std::transform(animals.begin(), animals.end(),
               std::back_insert_iterator<std::vector<Dog*>>(dogs),
               [](auto ptr) { return static_cast<Dog*>(ptr); });