Как вставить дублирующий элемент в вектор?
Я пытаюсь вставить копию существующего элемента vector
, чтобы удвоить его. Следующий код работал в предыдущих версиях, но с ошибкой в Visual Studio 2010.
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
vector<int> test;
test.push_back(1);
test.push_back(2);
test.insert(test.begin(), test[0]);
cout << test[0] << " " << test[1] << " " << test[2] << endl;
return 0;
}
Вывод -17891602 1 2
, ожидаемый 1 1 2
.
Я выяснил, почему это происходит - вектор перераспределяется, и ссылка становится недействительной, прежде чем она будет скопирована в точку вставки. Более старая Visual Studio, по-видимому, делала что-то в другом порядке, доказывая тем самым, что один из возможных результатов поведения undefined заключается в том, чтобы работать правильно, а также доказывая, что он никогда не должен полагаться на него.
Я придумал два разных способа решить эту проблему. Один из них - использовать reserve
, чтобы убедиться, что перераспределение не происходит:
test.reserve(test.size() + 1);
test.insert(test.begin(), test[0]);
Другой - сделать копию из ссылки так, чтобы не было никакой зависимости от ссылки, остающейся действительной:
template<typename T>
T make_copy(const T & original)
{
return original;
}
test.insert(test.begin(), make_copy(test[0]));
Хотя оба работают, никто не чувствует себя естественным решением. Что-то мне не хватает?
Ответы
Ответ 1
Проблема заключается в том, что vector::insert
берет ссылку на значение как второй параметр, а не значение. Вам не нужен шаблон для создания копии, просто используйте конструктор копирования для создания другого объекта, который будет передаваться по ссылке. Эта копия остается действительной, даже если вектор изменен.
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
vector<int> test;
test.push_back(1);
test.push_back(2);
test.insert(test.begin(), int(test[0]));
cout << test[0] << " " << test[1] << " " << test[2] << endl;
return 0;
}
Ответ 2
Я считаю, что это определенное поведение. В §23.2.3
стандарта 2011 С++ в таблице 100 перечислены требования контейнера последовательности, и в этом случае есть запись. Он дает примерное выражение
a.insert(p,t)
где a
- это значение X
, которое является типом контейнера последовательности, содержащим элементы типа T
, p
является константным итератором с a
, а T
является значением lvalue или const rvalue типа X::value_type
, т.е. T
.
Утверждение для этого выражения:
Требуется: T
должно быть CopyInsertable
в X
. Для vector
и deque
, T
также должно быть CopyAssignable
. Эффекты: Вставляет копию T
до p
.
Единственная соответствующая векторная конкретная цитата, которую я мог найти, находится в пункте §23.3.6.5
:
Примечания: вызывает перераспределение, если новый размер больше старой. Если перераспределение не происходит, все итераторы и ссылки до точки вставки остаются в силе.
Хотя это и говорит о перераспределении вектора, он не делает исключение из предыдущих требований для insert
в контейнерах последовательностей.
Что касается работы над этой проблемой, я согласен с предложением @EdChum просто сделать копию элемента и вставить эту копию.