Ответ 1
По существу, случай для переменных одинаков для функций. Идея состоит в том, что мы храним результат вызова функции с переменной decltype(auto)
:
decltype(auto) result = /* function invocation */;
Тогда result
- это
тип без ссылки, если результат является предварительным значением,
(возможно, cv-квалифицированный) ссылочный тип lvalue, если результатом является lvalue, или
ссылочный тип rvalue, если результатом является xvalue.
Теперь нам нужна новая версия forward
, чтобы различать регистр prvalue и регистр xvalue: (имя forward
избегается для предотвращения проблем ADL)
template <typename T>
T my_forward(std::remove_reference_t<T>& arg)
{
return std::forward<T>(arg);
}
А затем используйте
my_forward<decltype(result)>(result)
В отличие от std::forward
, эта функция используется для пересылки decltype(auto)
переменных. Следовательно, он не обязательно возвращает ссылочный тип, и предполагается, что он вызывается с помощью decltype(variable)
, который может быть T
, T&
или T&&
, так что он может различать lvalue, xvalues и prvalues. Таким образом, если result
равен
не ссылочный тип, затем вторая перегрузка вызывается без ссылки
T
, и возвращается не ссылочный тип, что приводит к prvalue;ссылочный тип lvalue, затем первая перегрузка вызывается с помощью
T&
, и возвращаетсяT&
, что приводит к lvalue;ссылочный тип rvalue, затем вторая перегрузка вызывается с
T&&
, и возвращаетсяT&&
, что приводит к значению xvalue.
Вот пример. Учтите, что вы хотите обернуть std::invoke
и распечатать что-нибудь в журнал: (пример только для иллюстрации)
template <typename F, typename... Args>
decltype(auto) my_invoke(F&& f, Args&&... args)
{
decltype(auto) result = std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
my_log("invoke", result); // for illustration only
return my_forward<decltype(result)>(result);
}
Теперь, если выражение вызова
prvalue, тогда
result
является не ссылочным типом, а функция возвращает не ссылочный тип;неконстантное lvalue, тогда
result
является неконстантной lvalue ссылкой, а функция возвращает неконстантный lvalue ссылочный тип;const lvalue, тогда
result
является ссылкой const lvalue, и функция возвращает тип ссылки const lvalue;xvalue, тогда
result
является ссылочным типом rvalue, и функция возвращает ссылочный тип rvalue.
Учитывая следующие функции:
int f();
int& g();
const int& h();
int&& i();
справедливы следующие утверждения:
static_assert(std::is_same_v<decltype(my_invoke(f)), int>);
static_assert(std::is_same_v<decltype(my_invoke(g)), int&>);
static_assert(std::is_same_v<decltype(my_invoke(h)), const int&>);
static_assert(std::is_same_v<decltype(my_invoke(i)), int&&>);
(живая демонстрация, переместить только тестовый пример)
Если вместо этого использовать auto&&
, в коде возникнут проблемы с разграничением значений prvalue и xvalue.