如何避免使用插入迭代器调用复制构造函数

时间:2010-09-12 04:10:15

标签: c++ iterator containers insert-iterator

template<typename OutputIterator>
void BlitSurface::ExtractFrames(OutputIterator it,
                                int frame_width, int frame_height,
                                int frames_per_row, int frames_per_column,
                                bool padding) const
{
    SDL_Surface ** temp_surf = SDL_Ex_ExtractFrames(_surface, frame_width, frame_height, frames_per_row, frames_per_column, padding);

    int surface_count = frames_per_row * frames_per_column;

    for(int i=0; i<surface_count; ++i)
    {
        BlitSurface bs;
        bs._surface = temp_surf[i];
        *it = bs;
        ++it;
    }

    delete [] temp_surf;
}

我有这个功能,工作正常。唯一的问题是我不想调用复制构造函数,因为它复制整个表面,我只需要复制指针。我只想使用默认构造函数,然后将成员_surface设置为temp_surface [i],如下所示:

for(int i=0; i<surface_count; ++i)
{
    it->_surface = temp_surf[i];
    ++it;
}

适用于普通迭代器,但不适用于插入迭代器。如何修复它以适用于两者?

2 个答案:

答案 0 :(得分:1)

您真正想要的是移动InputIterator以与插入OutputIterator一起使用。由于C ++ 03中不存在这种情况,因此需要另一种方式来表示需要“浅”移动,而不是“深”复制。

对象本身中的简单状态标志将不起作用,因为允许实现在将对象实际放入容器之前随机复制对象。 (为了优化,你知道它不会,但不要担心调试版本。)

在我的头顶,听起来像是自定义分配器的工作。使用placement new的默认分配器copy-constructs;你可以定义一个替代构造函数,并使用placement new来调用它。

template< typename T >
struct move_traits {
    typedef T must_copy_type; // does not exist in specializations
};

template< typename T >
struct move_if_possible_allocator
    : std::allocator< T > {
    typedef move_traits<T> traits;

        // SFINAE selects this function if there is a specialization
    void construct( typename traits::may_move_type *obj, T &value ) {
        new( obj ) T(); // default construct
        traits::move_obj( *obj, value ); // custom routine
    }

        // SFINAE selects this function if traits is the base template
    void construct( typename traits::must_copy_type *obj, T const &value ) {
        new( obj ) T( value ); // copy construct (fallback case)
    }

    // define rebind... exercise for the reader ;v)
};

template<>
struct move_traits< BlitSurface > {
    typedef T may_move_type; // signal existence of specialization
    static void move_obj( BlitSurface &out, BlitSurface &in ) {
        // fill out and clear in
    }
}

当然,如果某些对象实际上被复制到容器中,那么将状态添加到BlitSurface到禁用移动move_obj是完全正常的。

答案 1 :(得分:0)

提到正在调用复制构造函数。在提供的示例中,似乎容器可能被定义为容纳BlitSurface。像std :: vector&lt; BlitSurface取代。这是我从以下几行中猜到的:

    BlitSurface bs;
    bs._surface = temp_surf[i];
    *it = bs;

我的理解是所有std容器都会在插入时复制。从那里开始,您可以通过引用使用容器中的对象。如果你不想在BlitSurface上调用复制构造函数,那么我建议容器存储一个指向BlitSurface的指针。这样当容器在复制时插入实际复制的对象就是一个指针(不是指向的BlitSurface对象)。

    BlitSurface* bs = new BlitSurface;
    bs->_surface = temp_surf[i];
    *it = bs;

请记住,这种方法在堆上分配(即新的),因此必须稍后显式删除内存,或者可以在容器中使用某种类型的智能指针来处理删除(std :: vector&lt; boost :: shared_ptr&lt; BlitSurface&gt;&gt;)。