Может ли сохранение возвращаемых значений в ссылках работать хуже всего? - PullRequest
0 голосов
/ 11 июня 2018

Я пишу очень универсальный код и обрабатываю возвращаемое значение из вызовов функций как const auto&.Так, например:

const auto& value = foo();

Цель состоит в том, чтобы написать его наиболее общим способом, чтобы он не делал никаких предположений относительно типа возврата foo, который может возвращаться по значению или возвращатьсяпо ссылке, не нарушая ничего в коде клиента, даже если значение является временным.

Но может ли этот код работать хуже, чем:

const auto value = foo();

В случае, если foo() возвращает базовый типтакие как int или double или enum?

Ответы [ 4 ]

0 голосов
/ 12 июня 2018

Исходя из этого комментария , решение поваренной книги C ++ 17 для работы с универсальными функторами, которые принимают перенаправленные параметры, выглядит примерно так:

  1. Если вам нужен их возвратзначение, с категорией значения и тем, что не сохранено, используйте переменную decltype(auto), чтобы захватить его.
  2. Иметь тип возврата ваших собственных функций будет decltype(auto).
  3. Использовать std::invoke для упрощения вызова сайта.И чтобы действительно поддерживать все виды любопытных типов функторов.
  4. Используйте std::invoke_result для обработки углового случая, где возвращаемый тип void.Вы не хотите определять переменные типа void.

Все, что он делает для этого шаблона (шаблона):

#include <type_traits>
#include <functional>
#include <utility>

template<class F, typename... Args>
decltype(auto) process_call(F&& f, Args&&... args) {
  if constexpr (std::is_void_v<std::invoke_result_t<F, Args...>>) {
    std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
    // Process without capturing 
    return;
  }
  else {
     decltype(auto) ret = std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
     // Use ret for your processing
     return ret;
  }
}
0 голосов
/ 11 июня 2018

Я понимаю, почему вы можете подумать, что это потребует затрат времени выполнения, однако на практике это не так.

Любой современный компилятор мгновенно сворачивает это в тип значения, если функция возвращает тип значениявместо того, чтобы сделать это ссылкой.Ваш const int& просто станет int после компиляции.Вы можете проверить это здесь: https://godbolt.org/g/vT2FdG.

0 голосов
/ 11 июня 2018

Это зависит.

Во многих случаях, особенно когда foo() возвращает тип примитива, ссылка будет оптимизирована, и между обеими версиями не будет никакой разницы.

Но еслиfoo() возвращает объект большого размера, это может зависеть от того, применимо ли NRVO или нет.

Конструкция auto value = foo(); включает NRVO, а const auto& value = foo(); может иногда предотвращать его ( demo ).Таким образом, нереференсная версия может быть быстрее.

Но если NRVO невозможно из-за способа написания foo(), временный объект может быть создан и скопирован.Таким образом, ссылочная версия будет быстрее.

Примечание: если возвращаемый объект не может быть создан для копирования, то не ссылочная версия не будет скомпилирована.

0 голосов
/ 11 июня 2018

Лучше всего ставить на пробную результаты для простых типов данных, включая проверку сгенерированной сборки на целевых платформах.

Для меня инстинкт предполагает, что

const int value = foo();

будет быстрее , чем

const int& value = foo();

, поскольку накладные расходы на настройку эталона, включая вероятность продления срока службы, могут бытьбольше, чем значение значения простого типа данных старого типа.

Где отсечка, если таковая имеется, будет зависеть от платформы, и ваш оптимизирующий компилятор может выровнять игровое поле в любом случае.

...