【问题标题】:Passing std::vector of derived type where a std::vector of base type is expected传递派生类型的 std::vector,其中需要基类型的 std::vector
【发布时间】:2017-04-10 18:02:21
【问题描述】:

我有一个基类:

class Member
{
    ....
}

还有一个派生类,像这样:

class Mine_sweeper : private Member
{
    ....
}

然后我有一个容器:

class Population
{
public:
    std::vector<Member>& m_members;
    Population(std::vector<Member>& members);
}

当我将 Population 的构造函数与成员向量一起使用时,它可以编译。但是,如果我尝试将 Population 的 m_members 与 Mine_sweepers 的向量联系起来,编译器会抛出错误。

std::vector<Member> members;
Population pop(members); // this compiles OK

std::vector<Mine_sweeper> sweepers;
Population pop(sweepers); // but here I get an error
main.cpp(23): error C2664: 'Population::Population(std::vector &)' : 无法将参数 1 从 'std::vector' 转换为 'std::vector &'

【问题讨论】:

    标签: c++ polymorphism


    【解决方案1】:

    Mine_sweepers 的向量不是成员的“种类”向量,即使 Mine_sweeper 是“种类”的成员。您不能使用vector&lt;Derived&gt; 代替vector&lt;Base&gt;。

    使用vector&lt;unique_ptr&lt;Member&gt;&gt; 代替vector&lt;Member&gt; 和vector&lt;Mine_sweeper&gt;。这使您能够拥有异构的成员群体(即同时拥有 Mine_sweepers 和其他成员子类型)。

    或者,将 Population 类转换为类模板:

    class Population<T>
    {
    public:
        std::vector<T>& m_members;
        Population(std::vector<T>& members);
    }
    

    这样,一个 Population 实例可能只包含一种类型的成员。

    【讨论】:

    • @A.Matric "将 Population 类转换为类模板"。这就是我所说的,但YSC的答案更好。
    【解决方案2】:

    你不能。不一样。这是因为std::vector&lt;B&gt; 不是从std::vector&lt;A&gt; 派生的,而不管B 是从A 派生的事实。

    但是等等……让我们用一个最小的例子来简化你的问题:

    struct A {};
    struct B : A {};
    void f(std::vector<A> const&) {}
    
    int main()
    {
        std::vector<A> vecA;
        std::vector<B> vecB;
        f(vecA);
        f(vecB); // error
    }
    

    这里,与您的示例代码不同,B 公开继承自 A。您仍然不能将std::vector&lt;B&gt; const&amp; 提供给期望std::vector&lt;A&gt; const&amp; 的函数因为不存在从std::vector&lt;B&gt; 到std::vector&lt;A&gt; 的隐式转换。

    在以下情况下,可以发送B const&amp;,因为 C++ 提供了从派生类型引用到基类型引用的隐式转换。

    struct A {};
    struct B : A {};
    void f(A const&) {}
    
    int main()
    {
        A alice;
        B bob;
        f(alice);
        f(bob);
    }
    

    一种解决方案是使用现有的从B 到A 的隐式转换来提供显式转换:

    struct A {};
    struct B : A {};
    class Container
    {
        std::vector<A*> _items;
    public:
        template<class T>
        Container(std::vector<T> v)
            : _items(v.begin(), v.end())
        {}
        // other stuff, see rule of 5
    };
    
    
    int main()
    {
        std::vector<A*> vecA;
        std::vector<B*> vecB;
    
        Container contA(vecA);
        Container contB(vecB);
    }
    

    完成基本的工作转换后,您需要弄清楚如何使用std::unique_ptr、std::shared_ptr 和std::weak_ptr 处理内存所有权。

    【讨论】:

      【解决方案3】:

      Population 类的构造函数将参数传递给包含 Member 类数组的向量。你是对的,你可以将派生类传递给以基类作为参数的东西,或者从派生向上转换到基类,但是你在这里做的是不同的。您不是将派生类发送到采用基类参数的函数,而是完全发送了不同的不相关类型,而是将 std::vector 发送到需要 std::vector 的东西.

      当您在编译时创建 std::vector 时,它将创建向量类,其中包含特定于它如何迭代整数等的所有特定细节。这是一个数组或“整数”。将它传递给一个接受双精度数组的函数是否有意义?然后该函数正在处理双精度数,例如移动到下一个双精度数它可能会向前移动 8 个字节而不是 4 个字节,它会做错事。

      您必须手动制作两个版本的 Population 类或使用模板来完成。编辑:见 YSC 的回答,他模板化了构造函数

      或者,您可能想要一个向量/数组,它可以包含两种类型的类,Base 和 Minesweeper。为此,您不能将对象本身存储在数组中,因为这两种类型的对象可能大小不同并且具有不同的成员。所以你可以有一个指向基类的指针向量。然后,多态性部分用于识别,当您取消引用其中一个指针时,您是指向 Base 还是 Minesweeper。虚函数用于根据它是哪个对象调用正确的函数。

      【讨论】:

        【解决方案4】:

        您不能只将派生类对象的向量传递给需要基类对象向量的函数,因为向量不是从向量派生的。

        你可以做一些转换,得到一片向量,像这样:

        vector&lt;Base&gt; converted(base_vec.begin(), base_vec.end());

        但这效率不高,如果调用方法在你的控制之下,你可以直接使用模板:

        template <typename T>
        void foo(const vector<T>& v) {
            v[0].bar(); // do something with v
        }
        

        如果你不想要其他类型有 bar 但不是 Base 的派生类,你可以使用 std::enable_if:

        template<typename T, typename Enable=std::enable_if<std::is_base_of<Base, T>::value>>
        void foo(const std::vector<T>& v) {
            v[0].bar(); // do something with v
        }
        

        整个程序可以用gcc main.cc -std=c++11编译:

        #include <vector>
        
        class Base{
        public:
            void bar() const {}
        };
        
        class Derived: public Base{
        };
        
        template<typename T, typename Enable=std::enable_if<std::is_base_of<T, Base>::value>>
        void foo(const std::vector<T>& v) {
            v[0].bar();
        }
        
        int main() {
            std::vector<Derived> a(1);
            foo(a);
        }
        

        【讨论】:

          【解决方案5】:

          您还可以将std::transform 与std::back_inserter 一起使用,请参阅[https://stackoverflow.com/a/33522799/638048]

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-02-19
            • 1970-01-01
            • 2019-10-12
            • 2014-07-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多