2015-11-16 1 views
1

Если у меня есть тип вроде этого:Копирование векторов без использования элемента оператора присваивания копии - портативность

class Foo { 
public: 
    Foo(); 
    Foo(const Foo&); 
    Foo& operator=(const Foo&) = delete; 

    ... 
private: 
    ... 
}; 

И у меня есть два вектора такого типа:

std::vector<Foo> x; 
std::vector<Foo> y; 

И я хочу, чтобы скопировать содержание x до y, есть ли кросс-платформенный способ сделать это?

VC++ будет делать это с помощью y.assign(x.begin(), x.end()), который вместо конструктора копирования удаленных копий использует конструктор копирования Foo. Но GCC жалуется на отсутствующий оператор присваивания копий, попробуйте ли вы y = x или y.assign(x.begin(), x.end()).

Есть ли способ, который будет работать в обоих?

+0

Стандарт требует, чтобы 'T' быть копия назначаемые для' станд :: вектор :: Assign() 'работать, даже если VC++ неправильно делает not.Can вы объяснить, почему вы хотите класс 'Foo' не имеет оператора присваивания, но все же может копировать/назначать объекты типа' std :: vector '? – Peter

+0

@Peter Если 'T' имеет член const, он может быть сконструирован, но не назначен позже. Следовательно, может быть разумным определить конструктор копирования, но не оператор присваивания копии. Является ли это значимым (или полезным), зависит от приложения. – Andrew

+0

Есть ли конструктор перемещения и оператор переадресации? –

ответ

3

Стандарт требует, чтобы для y.assign(it1, it2) работать, тип T элемент будет CopyAssignable. То есть копировать конструктивно и присваивать. Ваш тип не может быть назначен, поэтому вы не можете положиться на assign. То же самое касается y = x. Раздел 23.2.3 стандарта описывает требования к различным операциям контейнера последовательности.

Если у вас есть существующий вектор y, вы можете построить новый вектор z переставить с y:

{ 
    std::vector<Foo> z(x.begin(), x.end()); 
    z.swap(y); 
} 

Это использует конструктор диапазона, который требует только то, что T быть EmplaceConstructible, ни один оператор присваивания не нужен ! Затем вы можете заменить базовую память без дополнительных копий.

Обратите внимание, что это может привести к увеличению использования памяти, поскольку любое существующее содержимое y будет сохраняться до тех пор, пока свежее обмен данными z не выйдет из сферы действия. Вы могли бы попытаться смягчить это сначала делает

y.clear(); 
y.shrink_to_fit(); 

хотя shrink_to_fit() только запрос, и не может быть удостоин вашей реализации библиотеки.

[Live demo]

2

До C++ 11 ваш код не был бы возможен: тип векторного элемента должен был быть CopyAssignable, что означает возможность копирования и назначение. Обратите внимание, что если тип не отвечает этим требованиям, код плохо сформирован без необходимости диагностики; это дает компиляторам широту для принятия или отклонения.

С C++ 11 индивидуальные операции имеют свои собственные требования. Соответствующие требования к vector являются (источник: C++ 14 Таблица 100):

  • emplace_back: MoveInsertible и MoveAssignable
  • insert: CopyInsertable и CopyAssignable
  • конструктор: EmplaceConstructible

Значения из этих признаков (примерно):

  • CopyInsertable: есть копия-конструктор
  • MoveInsertable: есть копировать-конструктор или переместить-конструктор (или оба)
  • CopyAssignable: есть копировать-конструктор и копирование назначение оператора
  • MoveAssignable: MoveInsertable, и имеет копию оператор -assignment или переместить присваивание (или оба)
  • EmplaceConstructible: есть конструктору

Обоснованием здесь заключается в том, что любая вставка может вызвать перераспределение памяти, которое требует, чтобы ваши объекты могли быть скопированы или перемещены. Однако Стандарт не требует, чтобы компилятор проверял, доступны ли конструктор или оператор присваивания, и использовать один, если другой нет. Вместо этого он указывает, что оба должны быть доступны.


Вы не указали, является ли ваш класс подвижным. Предполагая, что это не так, значит, вы не можете использовать какие-либо методы emplace или insert или любую другую функцию, которая может вызвать перераспределение.

Если вы создаете y в то время, то вы можете инициализации использования конечно:

vector<int> y = x; 

Если y уже существует, так как ваш вопрос, кажется, предполагает, то вы почти не повезло: вы можете 't использовать любые функции вставки, поэтому вы можете изменять только существующие элементы. И поскольку нет оператора присваивания, вы не можете сделать это с помощью назначения.

Но вы можете использовать y.swap(), как предложено в ответе Андрея.

Рассмотрите возможность добавления move-constructor и move-assign в свой класс или перепроектирование кода, чтобы вам не нужно было назначать y. (Например, используйте указатель).

Если x.size() == y.size(), то вы можете использовать размещение нового в последней инстанции хака:

for (size_t i = 0; i != y.size(); ++i) 
{ 
    y[i].~Foo(); 
    new(&y[i]) Foo(x[i]); // copy-construct 
} 
Смежные вопросы