Есть ли лишнее копирование в std::optional? C++
Открыл для себя такую полезную штуку, как std::optional и std::expected. При прочтении документации по методу or_else() заметил, что он возвращает optional по значению. Значит ли это, что при каждом вызове or_else(), если optional содержит значение, то все его содержимое копируется/перемещается в новый экземпляр при возврате из функции? Например такой код:
// Методы хотят добавить в С++23, но, предположим, есть свой optional с такими методами
// Пусть foo() возвращает валидный std::optional
auto opt = foo().or_else([]{std::cout << "Invalid\n";})
.or_else([]{std::cout << "Invalid 2\n";});
// Перемещение содержимого optional при возврате значения из or_else()
// При условии, что optional содержит значение
И если из-за ref-квалификаторов с перемещаемыми объектами все нормально, то что делать с объектами, которые хранят большое количество данных на стеке в полях класса? Их ведь не переместить, они будут копироваться в новый optional
Ответы (1 шт):
Лишнее копирование/перемещение не запрещено, а даже требуется, и может быть исключено разве что через "as-if" правило.
Лишнее копирование/перемещение запрещено для случая optional не содержащего значение, если там внутри return неименованной переменной.
В общем случае оптимизация вряд ли возможна для с lvalue optional, т.к. после or_else можно получить независимые копии объекта, каждая со своим состоянием.
Для rvalue (т.е. для ...::or_else(...) &&) принципиально можно сделать оптимизацию в общем случае, трансформировав в:
void or_else(F f) {
if (!*this)
{
*this = f();
}
} // далее используем старый объект как якобы новый
И, возможно, какой-то будущий стандарт будет разрешать/требовать такую оптимизацию, как C++17 требует RVO, а предыдущие разрешают.