C++ - 多态性:用派生类初始化容器类

4

我有一个关于多态和可能的其他技术的问题。考虑以下方案:

#include <vector>
#include <memory>

using namespace std;

struct Base
{ };

struct Derived : Base
{ };

class group
{
    private:
        vector< unique_ptr<Base> > V;
    public:
        group( /* what to put here? */ )
        : V( /* again: how to construct V? */ )
        { }
}

这里有三个类:一个基类,叫做Base; 一个派生类,叫做Derived; 还有一个最后的类,group,它将充当一个包装器来保存Base指针。但我想要实现以下功能:

  • Use move semantics. There should be no copies involved. The caller should be able to give the constructor several temporaries, as the constructor would steal them:

    group my_wonderful_group( Base( /* parameters */ ) , Derived ( /* ... */ ) , Derived ( /* ... */ ) );
    
  • Initialize V in the initialization list. That way, V could be const-qualified (despite all the other benefits initializing member objects in initialization lists carries).

我已经尝试了几种方法,但它们似乎不合适,或者概念上与我的目标相距甚远: - initializer_list的元素是不可移动的;unique_ptr只能进行移动操作。 - 可变模板似乎无法符合使V成为const的目标。由于initializer_list不能保存不同类型的对象(就像vector一样),我最初考虑使用它们,但如何实现呢?阅读了许多示例后,我仍然无法弄清楚如何创建可变模板构造函数,甚至不知道是否可以在没有解决方案(例如:创建某个init()函数,该函数接受Base、Derived等参数并对其进行操作)的情况下完成此操作。 - 创建一个返回unique_ptr的函数,并将其参数转发到Derived的构造函数中,从而作为group类的用户友好包装器。然而,这似乎没有太大意义,正如我所附上的代码所说明的那样。
#include <string>
#include <vector>
#include <memory>
#include <initializer_list>
#include <iostream>

using namespace std;

struct Base
{
    string s;
    Base(Base && b) : s(move(b.s)) { }
    Base(const string & S) : s( S ) { }
};

struct Derived : Base
{
    Derived(const string & S) : Base( S ) { }
};

unique_ptr<Base>
BASE ( const string & S )
{
    return unique_ptr<Base>( new Base(S) );
}

unique_ptr<Base>
DERIVED ( const string & S )
{
    return unique_ptr<Base>( new Derived(S) );
}

class C
{
    private:
        vector< unique_ptr<Base> > V;
    public:
        template<typename ... T>
            C
            ( T ... t ) : V({ t... })
            { }

        void
            print
            ( void )
            {
                for ( const auto & x : this->V )
                    cout << x->s << endl;
            }
            ;
};


int main(void)
{
    C c( BASE("hola") , DERIVED("der1") , DERIVED("bor3") );
    c.print();
    return 0;
}

然而,它抱怨在group构造函数中展开并用逗号分隔放入初始化列表V的参数包中调用了unique_ptr的[deleted]复制构造函数。
我想我的问题归结为一个vector,实际上,这个问题可以应用于一个vector<unique_ptr<Base>>(/*用派生类初始化*/)。我认为这一定是以前解决过的,因为多态性是基本的C++,保存派生类对象似乎是非常常见的用法。顺便说一下,我使用的是g++ 4.8.1
提前感谢。最好的祝福, Kalrish
P.S.: 我刚刚阅读了这个问题,它似乎部分涵盖了我的问题。

你考虑过使用 Boost.Variant (www.boost.org/libs/variant) 吗?因为它似乎非常符合你想要实现的 group 类的需求。 - lip
请参考https://dev59.com/R2oy5IYBdhLWcg3wkO0K来解决向量初始化问题。 - Jarod42
1个回答

3
以下内容应该解决您的所有问题:
#include <iostream>
#include <memory>
#include <string>
#include <vector>

using namespace std;

struct Base
{
    string s;
    Base(const Base& b) = delete;
    Base(Base && b) : s(move(b.s)) { }
    Base(const string & S) : s( S ) { }
};

struct Derived : Base
{
    Derived(const string & S) : Base( S ) { }
};

#if 1 // not in C++11
template <typename T, typename ... Ts>
std::unique_ptr<T> make_unique(Ts&&...args)
{
    return std::unique_ptr<T>(new T{std::forward<Ts>(args)...});
}
#endif

// vector<move_only> cannot be construct from initializer list :-/
// Use this work around
template <typename Base, typename ... Ts>
std::vector<std::unique_ptr<Base>> make_vector_of_unique(Ts&&... ts)
{
    std::unique_ptr<Base> init[] = {make_unique<Ts>(std::forward<Ts>(ts))...};
    return std::vector<std::unique_ptr<Base>> {
        std::make_move_iterator(std::begin(init)),
        std::make_move_iterator(std::end(init))};
}

class C
{
private:
    const std::vector< std::unique_ptr<Base> > V;
public:
    template<typename ... Ts>
    C(Ts&& ... t) : V(make_vector_of_unique<Base>(std::forward<Ts>(t)...))
    {}

    void print()
    {
        for (const auto & x : this->V)
            std::cout << x->s << std::endl;
    }
};

int main() {
    C c( Base("hola") , Derived("der1") , Derived("bor3") );
    c.print();
    return 0;
}

太完美了!谢谢你! - djsp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接