【问题标题】:Can I cast std::vector<Animal*> to std::vector<Dog*> without looking at each element?我可以在不查看每个元素的情况下将 std::vector<Animal*> 转换为 std::vector<Dog*> 吗?
【发布时间】:2010-11-13 14:55:11
【问题描述】:

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

// 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));
    }
}

【问题讨论】:

  • 这不是骗人的——注意他不是从vector&lt;Derived*&gt;复制到vector&lt;Base*&gt;,而是反过来!
  • 我猜他正在寻找一种自动/隐含的沮丧!
  • 感谢您的反馈。 :) 不知道怎么问,是的,很难知道要搜索什么!

标签: c++ templates vector casting


【解决方案1】:

您可以使用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>() );
}

【讨论】:

  • 这还是“看每个元素”,只是流程排列不一样。
【解决方案2】:

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

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 );
  }
}

【讨论】:

    【解决方案3】:

    有两种选择。最简单的方法是使用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

    如果您的代码仅基于迭代器,另一种方法是将向量 的迭代器包装为仅返回集合中的狗的迭代器。这里的主要优势是您仍然只有一个容器,但您当然需要支付更多费用,因为您的算法将在整个动物集合中导航。

    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】:

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

      有关更多信息,请参阅CPP FAQ lite

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

      【讨论】:

        【解决方案5】:

        如果你说你可以保证每个元素都是真正的狗,那么只需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 中进行任何分配。作为替代循环,我使用索引只是因为我一直认为使用索引进行迭代必须比迭代器更快,但这可能是无稽之谈:)

        【讨论】:

          【解决方案6】:

          当您可以保证您的std::vector&lt;Animal*&gt; 仅包含Dog* 时,您可以使用reinterpret_cast

          【讨论】:

          【解决方案7】:

          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); });
          

          【讨论】:

            猜你喜欢
            • 2017-05-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-10-20
            • 2016-03-31
            • 2023-03-23
            相关资源
            最近更新 更多