Мне нравится измерять, поэтому я настроил это Object
:
#include <iostream>
struct Object
{
Object() {}
Object(const Object&) {std::cout << "Object(const Object&)\n";}
Object(Object&&) {std::cout << "Object(Object&&)\n";}
Object& makeChanges() {return *this;}
};
И я предположил, что некоторые решения могут давать разные ответы для значений x и значений prvalue (оба из которых являются значениями r).И поэтому я решил проверить их обоих (в дополнение к lvalues):
Object source() {return Object();}
int main()
{
std::cout << "process lvalue:\n\n";
Object x;
Object t = process(x);
std::cout << "\nprocess xvalue:\n\n";
Object u = process(std::move(x));
std::cout << "\nprocess prvalue:\n\n";
Object v = process(source());
}
Теперь просто попробовать все ваши возможности, предоставленные другими, и я бросил один в себя:
#if PROCESS == 1
Object
process(Object arg)
{
return arg.makeChanges();
}
#elif PROCESS == 2
Object
process(const Object& arg)
{
return Object(arg).makeChanges();
}
Object
process(Object&& arg)
{
return std::move(arg.makeChanges());
}
#elif PROCESS == 3
Object
process(const Object& arg)
{
Object retObj = arg;
retObj.makeChanges();
return retObj;
}
Object
process(Object&& arg)
{
return std::move(arg.makeChanges());
}
#elif PROCESS == 4
Object
process(Object arg)
{
return std::move(arg.makeChanges());
}
#elif PROCESS == 5
Object
process(Object arg)
{
arg.makeChanges();
return arg;
}
#endif
В таблице ниже приведены мои результаты (с помощью clang -std = c ++ 11).Первое число - это число конструкций копирования, а второе - количество конструкций перемещения:
+----+--------+--------+---------+
| | lvalue | xvalue | prvalue | legend: copies/moves
+----+--------+--------+---------+
| p1 | 2/0 | 1/1 | 1/0 |
+----+--------+--------+---------+
| p2 | 2/0 | 0/1 | 0/1 |
+----+--------+--------+---------+
| p3 | 1/0 | 0/1 | 0/1 |
+----+--------+--------+---------+
| p4 | 1/1 | 0/2 | 0/1 |
+----+--------+--------+---------+
| p5 | 1/1 | 0/2 | 0/1 |
+----+--------+--------+---------+
process3
выглядит для меня лучшим решением.Однако это требует двух перегрузок.Один для обработки lvalues и один для обработки rvalues.Если по какой-либо причине это проблематично, решения 4 и 5 выполняют работу только с одной перегрузкой, затрачивая на 1 дополнительный ход конструкции для glvalues (lvalues и xvalues).Это суждение о том, хочет ли кто-то заплатить дополнительную конструкцию хода, чтобы сохранить перегрузку (и нет единого правильного ответа).
(ответил) Почему RVO выбивает последний вариант ине второй?
Чтобы RVO включился, оператор return должен выглядеть следующим образом:
return arg;
Если вы усложните это с помощью:
return std::move(arg);
или:
return arg.makeChanges();
, тогда RVO блокируется.
Есть ли лучший способ сделать это?
Мои любимые p3 и p5,Мое предпочтение p5 перед p4 просто стилистическое.Я избегаю ставить move
на return
утверждение, когда знаю, что оно будет применено автоматически из-за страха случайного блокирования RVO.Однако в p5 RVO в любом случае не является опцией, хотя оператор return действительно получает неявный ход.Таким образом, p5 и p4 действительно эквивалентны.Выберите свой стиль.
Если бы мы передали временную опцию, 2-й и 3-й варианты вызвали бы конструктор перемещения при возврате.Возможно ли устранить это, используя (N) RVO?
Столбец "prvalue" против столбца "xvalue" решает этот вопрос.Некоторые решения добавляют дополнительную конструкцию перемещения для значений xvalues, а некоторые - нет.