【问题标题】:Getting a vector<Derived*> into a function that expects a vector<Base*>将 vector<Derived*> 放入需要 vector<Base*> 的函数中
【发布时间】:2010-09-12 00:06:25
【问题描述】:

考虑这些类。

class Base
{
   ...
};

class Derived : public Base
{
   ...
};

这个函数

void BaseFoo( std::vector<Base*>vec )
{
    ...
}

最后是我的向量

std::vector<Derived*>derived;

我想将derived 传递给函数BaseFoo,但编译器不让我这样做。在不将整个向量复制到 std::vector&lt;Base*&gt; 的情况下,如何解决这个问题?

【问题讨论】:

  • 我认为您需要澄清 BaseFoo 是打算通过 const 引用还是非常量引用来获取向量(按值获取它是非常不寻常的)。也就是说BaseFoo需要修改vector吗?

标签: c++ stl vector covariance


【解决方案1】:

一种选择是使用模板

template<typename T>
void BaseFoo( const std::vector<T*>& vec)
{
 ...
}

缺点是实现必须在标题中,你会得到一点代码膨胀。您最终会为每种类型实例化不同的函数,但代码保持不变。根据用例,这是一个快速而肮脏的解决方案。

编辑,我应该指出我们在这里需要一个模板的原因是因为我们试图为不相关的类型编写与其他几位发帖人所指出的相同的代码。模板允许您解决这些确切的问题。我还更新了它以使用 const 引用。当您不需要副本时,您还应该通过 const 引用传递“重”对象,例如向量,这基本上总是如此。

【讨论】:

  • 可能允许编译器通过实现生成的目标代码完全相同,将函数的两个实例优化为一个。怀疑许多人会使用复杂的功能来做到这一点。
  • 您可以通过使用显式实例化来避免在头文件中定义函数的需要。您可以将我的示例(如下)合并到您的答案中。
【解决方案2】:

它们是不相关的类型——你不能。

【讨论】:

    【解决方案3】:

    通常你会从一个基指针容器开始,而不是相反。

    【讨论】:

    • 我认为这两种情况在实践中都会发生。但我同意 vector 在良好的 OO 设计中更常见。
    【解决方案4】:

    vector&lt;Base*&gt;vector&lt;Derived*&gt; 是不相关的类型,所以你不能这样做。这在 C++ 常见问题解答 here 中进行了解释。

    您需要将变量从 vector&lt;Derived*&gt; 更改为 vector&lt;Base*&gt; 并将 Derived 对象插入其中。

    另外,为避免不必要地复制vector,您应该通过常量引用而不是值来传递它:

    void BaseFoo( const std::vector<Base*>& vec )
    {
        ...
    }
    

    最后,为避免内存泄漏,并使您的代码异常安全,请考虑使用旨在处理堆分配对象的容器,例如:

    #include <boost/ptr_container/ptr_vector.hpp>
    boost::ptr_vector<Base> vec;
    

    或者,将向量更改为保存智能指针而不是使用原始指针:

    #include <memory>
    std::vector< std::shared_ptr<Base*> > vec;
    

    #include <boost/shared_ptr.hpp>
    std::vector< boost::shared_ptr<Base*> > vec;
    

    在每种情况下,您都需要相应地修改您的 BaseFoo 函数。

    【讨论】:

      【解决方案5】:

      如果您与第三方库打交道,这是您唯一的希望,那么您可以这样做:

      BaseFoo (*reinterpret_cast<std::vector<Base *> *>(&derived));
      

      否则,请使用其他建议之一修复您的代码。

      【讨论】:

      • 虽然这可能确实有效,但感觉就像是一个定时炸弹。除非我确切地知道自己在做什么,否则我很犹豫是否要使用 reinterpret_cast。
      • 这是问题的唯一答案。其他答案基本上是“避免这个问题”——这些都是很好的建议,但并不总是适用。它可能更简单一点:BaseFoo(reinterpret_cast<:vector> &>(derived));
      【解决方案6】:

      像其他 STL 算法一样,传递 beginend 迭代器,而不是传递容器对象 (vector&lt;&gt;)。接收它们的函数将被模板化,传入 Derived* 或 Base* 都没有关系。

      【讨论】:

      • +1:没有提升或共享指针最简单的事情(我还不知道)
      【解决方案7】:

      如果std::vector 支持您的要求,那么可以在不使用任何强制转换的情况下击败 C++ 类型系统(编辑:ChrisN 到 C++ FAQ Lite 的链接讨论了同样的问题):

      class Base {};
      class Derived1 : public Base {};
      class Derived2 : public Base {};
      
      void pushStuff(std::vector<Base*>& vec) {
          vec.push_back(new Derived2);
          vec.push_back(new Base);
      }
      
      ...
      std::vector<Derived1*> vec;
      pushStuff(vec); // Not legal
      // Now vec contains a Derived2 and a Base!
      

      由于您的BaseFoo() 函数按值获取向量,它无法修改您传入的原始向量,所以我写的内容是不可能的。但是如果它需要一个非常量引用并且你使用reinterpret_cast&lt;std::vector&lt;Base*&gt;&amp;&gt;() 来传递你的std::vector&lt;Derived*&gt;,你可能不会得到你想要的结果,并且你的程序可能会崩溃。

      Java 数组支持covariant subtyping,这需要Java 支持do a runtime type check every time you store a value in an array。这也是不可取的。

      【讨论】:

        【解决方案8】:

        从上面的Matt Price's 回答中,假设您事先知道要在函数中使用什么类型,您可以在头文件中声明函数模板,然后为这些类型添加显式实例化:

        // BaseFoo.h
        template<typename T>
        void BaseFoo( const std::vector<T*>& vec);
        
        // BaseFoo.cpp
        template<typename T>
        void BaseFoo( const std::vector<T*>& vec);
        {
         ...
        }
        
        // Explicit instantiation means no need for definition in the header file.
        template void BaseFoo<Base> ( const std::vector<Base*>& vec );
        template void BaseFoo<Derived> ( const std::vector<Derived*>& vec );
        

        【讨论】:

          【解决方案9】:

          此问题发生在具有可变容器的编程语言中。你不能将一袋可变的苹果当作一袋水果来传递,因为你不能确定其他人没有将柠檬放入那袋水果中,之后它就不再符合一袋苹果的条件。如果一袋苹果不是可变的,那么将它作为一袋水果传递就可以了。搜索协变/逆变。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-08-22
            • 2021-11-28
            • 2021-10-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多