Как избежать вызова конструктора копирования с итераторами вставки - PullRequest
0 голосов
/ 12 сентября 2010
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 ]

1 голос
/ 12 сентября 2010

Действительно, вы хотите переместить InputIterator для использования с вставкой OutputIterator. Поскольку этого нет в C ++ 03, должен существовать альтернативный способ указать, что желательно «мелкое» перемещение, а не «глубокое» копирование.

Простой флаг состояния в самом объекте не будет работать, потому что реализация позволяет копировать объект случайным образом, прежде чем фактически поместить его в контейнер. (Ради оптимизации вы знаете, что этого не произойдет, но не стоит беспокоиться об отладочных сборках.)

С моей головы это звучит как работа для пользовательского распределителя. Копируемые конструкции распределителя по умолчанию с использованием размещения new; Вы можете определить альтернативный конструктор и вызвать его, используя вместо этого размещение 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, если некоторые объекты фактически скопированы в контейнер.

0 голосов
/ 12 сентября 2010

Упоминается, что вызывается конструктор копирования.В приведенном примере кажется, что контейнер, вероятно, определен для хранения BlitSurface.Что-то вроде std :: vector .Это предположение с моей стороны из следующих строк:

    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 >).

...