#include <vector>
#include <iostream>
#include <iterator>
using namespace std;
class MoveableClass
{
public:
MoveableClass() {
cout << "Default constructor" << endl;
}
MoveableClass(const MoveableClass& src) {
cout << "Copy constructor" << endl;
}
MoveableClass(MoveableClass&& src) {
cout << "Move constructor" << endl;
}
MoveableClass& operator=(const MoveableClass& rhs) {
cout << "Copy assignment operator" << endl;
return *this;
}
MoveableClass& operator=(MoveableClass&& rhs) {
cout << "Move assignment operator" << endl;
return *this;
}
};
int main()
{
vector<MoveableClass> vecSource(3);
cout << "----" << endl;
MoveableClass mc;
cout << "----" << endl;
vecSource.push_back(mc);
// vecSource.push_back(mc);
// vecSource.push_back(mc);
// vecSource.push_back(mc);
cout << "----" << endl;
// Copy the elements from vecSource to vecOne
vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
cout << "----" << endl;
// Move the elements from vecSource to vecTwo
vector<MoveableClass> vecTwo(make_move_iterator(vecSource.begin()),
make_move_iterator(vecSource.end()));
cout << "----" << endl;
return 0;
}
Из приведенного выше кода у меня есть 2 сомнения:
Зачем перемещать ctor не вызывается из реализованного класса, когда я использую 2 функции push_back (mc) для копирования ctor 3 раза, т.е. 1 для первого нажатия, а для второго нажатия первого вектора изменяется (последовательно растет) в другое место памяти (которое должно иметь срабатывание для первого нажатия) 3-й для 2-го нажатия
Даже когда я инициализирую векторный объект размером 3, почему вызов для копирования ctor увеличивается до 4 для одного push_back (mc).
Вывод:
Default constructor
Default constructor
Default constructor
----
Default constructor
----
Copy constructor
Copy constructor
Copy constructor
Copy constructor
----
Copy constructor
Copy constructor
Copy constructor
Copy constructor
----
Move constructor
Move constructor
Move constructor
Move constructor
----
gcc версия, которую я использую:
> gcc version 4.7.3
ОБНОВИТЬ
Спасибо за ответы, я получаю где-то
для моей 1) точки, которую я хочу добавить
// MoveableClass(const MoveableClass& src) {
// cout << "Copy constructor" << endl;
// }
MoveableClass(MoveableClass&& src) noexcept {
cout << "Move constructor" << endl;
}
....
void fun() {
cout << "hello\n";
}
int main()
{
vector<MoveableClass> vecSource(3);
// vector<MoveableClass>::iterator it;
// vecSource.reserve(3);
cout << "----" << endl;
MoveableClass mc;
cout << "----" << endl;
mc.fun();
vecSource.push_back(mc);
// vecSource.push_back(move(mc));
// vecSource.push_back(move_if_noexcept(mc));
// vecSource.push_back(mc);
// vecSource.push_back(mc);
// vecSource.push_back(mc);
// for(it = vecSource.begin(); it != vecSource.end(); ++it )
// cout << (*it).fun() << endl;
cout << "----" << endl;
// Copy the elements from vecSource to vecOne
vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
cout << "----" << endl;
// Move the elements from vecSource to vecTwo
vector<MoveableClass> vecTwo(make_move_iterator(vecSource.begin()),
make_move_iterator(vecSource.end()));
cout << "----" << endl;
return 0;
}
Я редактировал код выше
// vecSource.push_back(move(mc)); I can call move ctor only
// vecSource.push_back(move_if_noexcept(mc)); I can call move ctor only
understood..
Если я комментирую конструктор копирования, я получаю ошибку компиляции
knils @knils-HP: IteratorAdapters $ g++ -g -std = С++ 0x MoveIterators.cpp
Внутренняя ошибка компилятора: снова введены процедуры регистрации ошибок.
Пожалуйста, отправьте полный отчет об ошибке, если это необходимо, с предварительно обработанным источником.
См. Инструкции. Предварительно обработанный источник, хранящийся в файле /tmp/ccHhV599.out, приложите его к вашему отчету об ошибке.
почему он дает эту ошибку, почему не использует свою копию по умолчанию ctor
для 2) точки, когда я инициализирую для размера 3, означает ли это, что 3 ячейки памяти инициализируются экземпляром класса?
for(it = vecSource.begin(); it != vecSource.end(); ++it )
cout << (*it).fun() << endl;
Я не могу использовать выше код. Он дает ошибку
MoveIterators.cpp:48:30: note: mismatched types 'const _CharT* and 'void
Чтобы добавить, я думаю, здесь лежит diff для изменения размера резерва, где резерв не вызывается по умолчанию ctor и оставляет память неинициализированной.
Я думаю, что лучше использовать резерв для некоторого пространства для вектора в той степени, в которой мы нуждаемся, чтобы он избегал регулярных обменов памяти. даже если он превысит его, появится новое место.
ОБНОВИТЬ
для частичного изменения кода
vector<MoveableClass> vecSource;
vecSource.push_back(mc);
vecSource.push_back(mc);
Я получаю сообщение o/p
Копировать конструктор Копировать конструктор Переместить конструктор
Меня здесь смущает порядок. Я ожидаю его Копировать конструктор Переместить конструктор Копировать конструктор
потому что для первого нажатия It инициализирует один размер (копия) для второго, он перераспределяет, поэтому перемещает существующую память в новое местоположение (перемещение), а копия второго нажатия в новом месте (копировании) компилятора отличает почему..
С уважением!
вектор изменяется (последовательно растет) в другое место памяти (которое должно было вызвать движение для первого нажатия)
std::vector
будет использовать конструктор move во время перераспределения, только если этот конструктор-конструктор объявлен с помощью спецификатора noexcept
или если нет доступного конструктора-копии (подробнее см. std::move_if_noexcept
):
Добавив следующие незначительные изменения:
MoveableClass(MoveableClass&& src) noexcept {
// ~~~~~~~^
cout << "Move constructor" << endl;
}
Вы получите результат:
Copy constructor
Move constructor
Move constructor
Move constructor
Спецификатор noexcept
сообщает реализации std::vector
что он может безопасно применять семантику move к ее контенту. В противном случае у вас не будет надежной гарантии безопасности, которая в основном заявляет, что вектор остается нетронутым, если перераспределение не удается из-за исключения:
§23.3.6.5 [vector.modifiers]/p1:
Требуется: если исключение генерируется иначе, чем конструктор копирования, переместите конструктор, оператор присваивания или оператор назначения перемещения из
T
или с помощью любой операцииInputIterator
никаких эффектов. Если исключение вызывается конструктором перемещения non-CopyInsertable T
, эффекты неCopyInsertable T
.
Кроме того, функция-член push_back
не будет пытаться переместить-построить новый элемент, если его аргумент не может быть связан ссылкой на non- const rvalue, если нет, то он возвращается к построению копии. Если вы хотите переместить-построить новый элемент на основе экземпляра mc
в вызове push_back
, вам нужно передать значение x mc
:
vecSource.push_back(std::move(mc));
// ~~~~~~~~^
Вывод:
Move constructor
Даже когда я инициализирую векторный объект размером 3, почему вызов для копирования ctor увеличивается до 4 для одного push_back (mc).
Первоначальная мощность vecSource
, что можно запросить с .capacity()
функцией - члена, предположительно установлена 3
в вашем случае, это означает, что любая попытку сохранить больше элементов приводят к необходимости перераспределения, который требует, чтобы все элементов уже сохраненный в векторе, который будет скопирован в новую ячейку памяти.
Можно избежать неожиданных перераспределений, зарезервировав достаточное количество места для хранения до вызовов push_back
которые следуют:
vector<MoveableClass> vecSource;
vecSource.reserve(4); // reserve a storage for 4 elements
vecSource.resize(3); // default-construct 3 elements
cout << "----" << endl;
MoveableClass mc;
vecSource.push_back(mc); // copy-construct 4th element
Вывод:
Default constructor
Default constructor
Default constructor
----
Default constructor
Copy constructor
2), когда я инициализирую для размера 3, означает ли это, что 3 ячейки памяти инициализируются экземпляром класса?
Да, задав начальную емкость в вызове векторного конструктора или используя функцию члена resize
, в С++ 11 вы получите это количество построенных по умолчанию (в С++ 03 - копирование из построенного по умолчанию элемента), которые готовы к доступу и использованию.
cout << (*it).fun() << endl;
Я не могу использовать выше код. Он дает ошибку
Вы не можете распечатать результат вызова функции, объявляющего void
как возвращаемый тип. Просто удалите часть cout
и скомпилируйте ее:
for(auto it = vecSource.begin(); it != vecSource.end(); ++it )
(*it).fun();
Если я комментирую конструктор копирования, я получаю ошибку компиляции
Для некоторых операций требуется, чтобы тип векторного элемента был CopyConstructible; в вашем коде это:
vecSource.push_back(mc);
//...
vector<MoveableClass> vecOne(vecSource.begin(), vecSource.end());
"Копировать конструктор Копировать конструктор Переместить конструктор." Меня здесь смущает порядок. Я ожидаю его "Копировать конструктор Move constructor Copy constructor"
Для следующего фрагмента кода:
vector<MoveableClass> vecSource;
vecSource.push_back(mc);
vecSource.push_back(mc);
Согласно вашему результату, происходит следующее:
vector
равна 0
.push_back
: mc
копируется в новое выделенное хранилище (конструктор копирования).push_back
: mc
пытается быть вставлен в очередь. Емкость vector
слишком мала, поэтому выделяется новое хранилище. Копия mc
вставляется в новое хранилище (конструктор копирования). Затем остальные элементы перемещаются в новое место памяти (Move constructor).Я не думаю, что порядок, в котором копирование-конструирование присоединенного элемента идет до перенастройки, задан Стандартом, а именно, как он реализован в libstdc++, который вы используете.
Боковые заметки:
Предпочитаете -std=c++11
to -std=c++0x
если компилятор поддерживает первое.
Вы не должны повторно использовать экземпляр, который уже был перемещен. Надеюсь, вы делаете это только для тестирования.
Я не понял ваши вопросы. Сожалею. Но, на мой взгляд, единственное место в коде, где может возникнуть вопрос, почему не используется конструктор move, - это то, где вы вызываете push_back
vecSource.push_back( mc );
В этом месте вектор перераспределяет память, чтобы разместить еще один элемент, который является копией mc
. Если бы вектор использовал бы конструктор перемещения для его уже существующих элементов, если в случае исключения состояние вектора было бы неопределенным. Использование конструктора копирования гарантирует, что даже в случае исключения состояние вектора будет действительным, поскольку исходные элементы не будут изменены.
Но если вы объявите конструктор перемещения как не выбрасывающее исключение, например
MoveableClass( MoveableClass &&src ) noexcept
{
std::cout << "Move constructor" << std::endl;
}
то выход будет
Copy constructor
Move constructor
Move constructor
Move constructor
noexcept
позволяет перемещать предыдущие три элемента, когда выpush_back
четвертый элемент (с автоматическим перераспределением).