когда я был маленьким, я тоже думал, что не надо писать return std::move(res). Компилятор умеет всякие NRVO. А если и не умеет, то вроде как всё равно должен попытаться переместить.
А недавно столкнулся с тем, что в таком коде
std::string or_throw(std::optional<std::string> v)
{
return v ? v.value() : throw std::runtime_error("");
}
строка копируется. Надо явно вызывать std::move(v).value(). Хотя ясно (по крайней мере мне) что объект скоро помрёт и его надо по возможности обобрать. Делаем вывод: если значение перед возвратом надо как-то обработать, значит переместить не можем. Идём дальше, такой код почему-то перемещает:
std::optional<std::string> wrap(std::string s)
{
return s;
}
хотя вроде вызывается промежуточный конструктор. Предыдущие выводы отвергаем, немного усложняем пример. Такой код уже копирует:
std::optional<std::string> not_empty(std::string s)
{
return !s.empty() ? s : throw std::runtime_error("");
}
Может это слишком сложно для компилятора. Упростим до тривиального:
std::string get_name(Person p)
{
return p.name;
}
Всё равно копия. Правда уже немного объяснимо — очень похоже на первый пример. В общем, помогите навести порядок в голове.
p.s. может другие компиляторы ведут себя по-другому. Это gcc, оптимизация включена.