Как избежать вызова конструктора копирования с итераторами вставки
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 ответа
Действительно, вы хотите переместить 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
, если некоторые объекты фактически скопированы в контейнер.
Упоминается, что вызывается конструктор копирования. В приведенном примере кажется, что контейнер, вероятно, определен для хранения 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