Как написать функцию шаблона, которая возвращает ссылку или значение?

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

template<typename T>
auto&& Func()
{
  if constexpr (some_compile_time_expression)
  {
    return GetReferenceFromSomewhere();
  }
  else
  {
    return GetValueFromSomewhere();
  }
}

Это отлично подходит для всех типов ссылок, но не работает для значений. Например, если GetValueFromSomewhere возвращает Foo, то компилятор выводит возвращаемый тип Func как Foo&& и предупреждает, что я возвращаю адрес временного.

Есть ли способ сделать эту работу, или я вынужден дразнить две ветки отдельно (через перегрузки функций или некоторые из них)?

Ответ 1

Используйте decltype(auto) для заполнителя типа return, он сохранит точную категорию значений функции, которую вы вызываете в операторе return

template<typename T>
decltype(auto) Func()
{
  if constexpr (some_compile_time_expression_dependent_on_T)
  {
    return GetReferenceFromSomewhere();
  }
  else
  {
    return GetValueFromSomewhere();
  }
}

Демо-версия

Ответ 2

Преторианский ответ идеален, но вы также можете узнать о std::conditional, который имеет более широкое использование. Например, рассмотрим переменную- data_ типа int и функцию-член, которая возвращает data_ либо по ссылке, либо по значению в зависимости от некоторого условия компиляции:

template <bool COND>
std::conditional_t<COND, int&, int> data() { return data_; }

Это было бы невозможно с помощью decltype(auto). Вы также можете использовать тот же метод для передачи аргументов функции по ссылке/значению:

template <bool COND>
void f(std::conditional_t<COND, int&, int> param);

Или вы можете переключаться между конструкторами копирования/перемещения:

class X {
    X(std::conditional_t<some_cond, const X&, X&&>) = default;
    X(std::conditional_t<some_cond, X&&, const X&>) = delete;
    ...
};

Так далее...