Я хотел бы спросить, как эффективно вернуть std::optional, и я хотел бы использовать std::make_optional().
Например, пусть этот фрагмент кода:
std::optional<Path> CreateCanonicalPath(const std::string_view& path)
{
std::error_code errorCode;
const auto result = std::filesystem::weakly_canonical(std::filesystem::u8path(path), errorCode);
return !errorCode ? std::make_optional(result) : std::nullopt;
}
Меня особенно интересует, есть ли какая-то оптимизация при переходе result в std::make_optional. Может лучше использовать std::make_optional(std::move(result))?
И предотвращает ли это какие-либо РВО или НВРО?
result — это локальная переменная, но она не совсем в операторе возврата, поэтому я предполагаю, что компилятор не может использовать перемещение сам по себе.
Вы создали какие-то измерения (эталонные тесты)? Как вы думаете, почему этот код нужно оптимизировать? Это дикая догадка, или вы профилировали свой производственный код, чтобы обнаружить узкие места? Из всего этого weakly_canonical будет самой медленной частью, так как взаимодействует с ОС.
Какой смысл заворачивать std::path в std::optional? В чем разница между возвращенным пустым optional и пустым path в случае ошибок? Насколько я знаю, optional используется в основном для типов, которые сами по себе не предоставляют «пустое» состояние (например, целые числа) или где пустое состояние может быть допустимым результатом (например, с std::string).
@DanielLangr: пустой путь не обязательно является недопустимым путем. Это действительный результат разрезания пути, объединения и нормализации, дифференцирования и т. д. Я не думаю, что это обязательно указывает на ошибку. Но у меня нет большого опыта с std::filesystem.
@JiříLechner Я тоже, но то, что ты пишешь, имеет смысл.
Почему вы хотите использовать make_Optional()? Это как-то мешает.
@MichaëlRoy, make_optional() - это заводской шаблон, он может позволить использовать некоторые оптимизации. Например, make_shared() делает это, и это довольно важно. Я не думаю, что это относится к make_optional(), но мне нравится, чтобы это соответствовало другим make_...() функциям.
@JiříLechner Это может позволить некоторую оптимизацию, но не в этом случае, так как это добавит std::Optional присваивание перемещения и, таким образом, отменит оптимизацию возврата на место.





Есть одно очевидное улучшение:
std::optional<Path> CreateCanonicalPath(const std::string_view& path)
{
std::error_code errorCode;
auto result = std::filesystem::weakly_canonical(std::filesystem::u8path(path), errorCode);
return !errorCode ? std::make_optional(std::move(result)) : std::nullopt;
}
Создание временного объекта const потребует использования конструкции копирования как части создания экземпляра возвращаемого std::optional. Эта настройка должна привести к использованию семантики перемещения.
После этого любые дальнейшие улучшения будут сильно зависеть от поведения компилятора. Это маловероятно, но возможно, что при сравнительном анализе можно наблюдать некоторые незначительные отклонения в производительности с альтернативным синтаксисом, например, такие как:
std::optional<Path> CreateCanonicalPath(const std::string_view& path)
{
std::error_code errorCode;
auto result = std::make_optional(std::filesystem::weakly_canonical(std::filesystem::u8path(path), errorCode));
if (errorCode)
result.reset();
return result;
}
Если будет определено, что компилятор решит исключить копию, как это разрешено NVRO, то это также стоит протестировать. Но только реальный бенчмаркинг даст полезные результаты.
Таким образом, во втором фрагменте будет использоваться семантика перемещения, поскольку функция возвращает локальную переменную. А в зависимости от компилятора может применяться даже NVRO.
Что-то связанное: youtu.be/dGCxMmGvocE?t=430