1

我的代码利用了一些肮脏的技巧来使它看起来像我认为的一个不错的界面。最重要的类 ,Worker旨在让调用者用临时对象构造它;然后构造函数将接受它们。我认为代码是不言自明的:

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

struct Base
{
    virtual void
        print
        ( void )
        {
            cout << "Base" << endl;
        };
};

struct Derived1 : public Base
{
    virtual void
        print
        ( void )
        {
            cout << "Derived1" << endl;
        };
};

struct Derived2 : public Base
{
    virtual void
        print
        ( void )
        {
            cout << "Derived2" << endl;
        };
};

class Worker
{
    private:
        /* Arrays can't hold references, and
         * vectors are homogenous, so the
         * only option is to use (smart) pointers. */
        vector< unique_ptr<Base> >
            V
            ;

        /* The dirty trick I spoke about. */
        template<typename T> void
            init
            ( T && t )
            {
                V.emplace_back( new T( forward<T>(t) ) );
                return;
            };
        template<typename T , typename ... U> void
            init
            ( T && t , U && ... u )
            {
                V.emplace_back( new T( forward<T>(t) ) );
                    /* The usage of std::move() is explained below. */
                init(move(u)...);
                return;
            };

    public:
        template<typename ... T>
            Worker
            ( T && ... t )
            {
                    /* Use std::move() because, inside the body
                     * of the function, the arguments are lvalues.
                     * If I hadn't put std::move(), the compiler
                     * would complain about an attempt of using
                     * _new_ with a reference type (above). */
                init(move(t)...);
                return;
            };

        void
            work
            ( void )
            {
                for ( const auto & x : V )
                    x->print();
                return;
            };
};

int
main
    ( void )
{
    /* The goal: be able to create an instance of Worker
     * passing temporaries to the constructor. No initializer_list
     * is involved, no copies are made; clean, fast moves. */
    Worker worker{ Derived1() , Base() , Derived2() };
    /* This should print "Derived1\nBase\nDerived2\n". */
    worker.work();

    return 0;
}

尽管它编译得很好(g++ 4.8.1)并且开箱即用,但我觉得这不是实现我的目标的最佳方式,我想摆脱那种烦人的感觉。有没有人为此找到另一种解决方法?有没有更好的方法,我的设计有什么缺点?

提前致谢。代码应该编译得很好,并展示了我希望如何设计我的界面以及为什么我使用了这些“技巧”。最好的问候, Kalrish

4

1 回答 1

1

尽管我仍然认为这个问题更好地属于Code Review,但这是您的“肮脏把戏”的替代版本

    template < typename T >
    int emplace_back(T&& t)
    {
        V.emplace_back( std::forward<T>(t) );
        return 0;
    }

    template<typename ... T>
        Worker
        ( T && ... t )
        {
            auto i = {emplace_back(new T{forward<T>(t)})...};
        };

或者,如果您想摆脱该成员函数:

public:
    template<typename ... T>
        Worker
        ( T && ... t )
        {
            using up = std::unique_ptr<Base>;
            auto f = [&](up&& p)
                { V.emplace_back(std::move(p)); return 0; };

            auto i = {f( up{new T{forward<T>(t)}} )...};
        };

i但是,您可能会因为未使用而收到警告。仅在构建允许进行包扩展的初始化程序列表时才需要此变量。


通常,我建议使用初始化列表而不是可变参数模板 ctor(这就是它们的用途),但它们不支持移出元素,因为它们不拥有自己的存储空间。

如果您想vector在 ctor 的 mem-initializer-list 中初始化 (通过上面的替代方法中的包扩展),也会出现同样的问题。

于 2013-08-02T16:14:08.157 回答