Я узнал о конструкторах ходов в течение последнего дня или около того, пытаясь придерживаться общего правила возврата по значению, как кажется большинству людей, и столкнулся с интересной (для меня) дилеммой.
Предположим, что у меня есть дорогой для конструирования / копирования класс 'C', в котором правильно определены конструктор копирования, оператор присваивания, конструктор перемещения и оператор присваивания перемещения.
Во-первых, этот фрагмент кода исключает конструктор копированиякак я и ожидал:
C make_c1() {
return C();
}
, как это:
C make_c2() {
C tmp;
return tmp;
}
и так (я передаю 1 или 2):
C make_c3(int a) {
return a == 1 ? make_c1() : make_c2();
}
Когда я дохожу до этого, у меня возникает проблема:
C make_c4(int a) {
C tmp;
return a == 1 ? make_c1() : tmp;
}
Передача 1 запускает RVO для результата make_c1, но передача 2 запускает конструктор копирования на tmp.
Изменение функции к следующему приводит к тому, что вместо tmp запускается конструктор перемещения:
C make_c5(int a) {
C tmp;
return a == 1 ? make_c1() : std::move(tmp);
}
Все замечательно и замечательно, кроме ...
В этих простых примерах RVO был запущенкрасный, как я и надеялся.
Однако, что если мой код немного сложнее и на некоторых компиляторах не вызывает RVO в этой последней функции?В этом случае мне нужно будет обернуть мой вызов make_c1 в std :: move, что сделает код менее эффективным на тех компиляторах, которые вызывают RVO.
Итак, мои вопросы:
- Почему конструктор перемещения не вызывался в make_c4, когда я возвращал свой локальный объект?(В конце концов, он скоро будет уничтожен).
- В функции make_c5 я должен возвращать результаты make_c1 по значению или путем его перемещения?(Чтобы избежать разных версий кода для разных компиляторов / платформ).
- Есть ли лучший способ кодировать конечную функцию, чтобы она правильно работала для разумной реализации компилятора?
Компилятор, с которым я играл, это GCC 4.5.3 на Cygwin.