При пересылке результата функции через функцию шаблона я столкнулся с различным поведением в отношении использования «оптимизации именованного возвращаемого значения (NRVO)» для clang и gcc. Вот фрагмент кода:
Foo provideFooAsTemporary()
{
return Foo{};
}
template <typename TFn>
auto forwardA(TFn &&fn)
{
auto result = fn();
return result;
}
template <typename TFn>
auto forwardB(TFn &&fn) -> decltype(fn())
{
auto result = fn();
return result;
}
Foo fooA = forwardA(provideFooAsTemporary);
Foo fooB = forwardB(provideFooAsTemporary);
Вот мои наблюдения (также см. пример с болтом):
gcc 14.1: Избегает любых операций перемещения как для forwardA
, так и для forwardB
.
clang 18.0.1: Избегает перемещения только для forwardB
, но операция перемещения выполняется для forwardA
.
Я ожидал, что forwardA
запускает NRVO также и для лязга.
Вот мои вопросы:
В чем именно разница между forwardA
и forwardB
? Почему указанный конечный тип возвращаемого значения имеет значение?
Есть ли более простой способ включить NRVO для clang, чем тот, который показан на forwardB
?
Удивительно, но это известная проблема в текущей реализации NRVO в Clang:
Если шаблон функции использует выведенный тип возвращаемого значения (т. е. auto
или decltype(auto)
), то NRVO не применяется ни для какой реализации этого шаблона функции.
См., например. отчеты об ошибках
и, возможно, еще больше других.
Насколько я понимаю, на самом деле нет никакой причины, по которой применение NRVO должно зависеть от того, указан ли явно тип возвращаемого значения. Кажется, это ограничение текущего подхода к реализации.
Однако с точки зрения соответствия NRVO никогда не гарантируется, и компилятор всегда волен не применять его. Его невозможно заставить это сделать.