转发参考:给定 T&& 时返回 T&& 和给定 T&&时返回 T&

Forwarding reference: returning T when given T&& and T& when given T&

本文关键字:返回 转发 给定 参考      更新时间:2023-10-16

考虑一个类A,我如何编写具有相同行为的模板

A& pretty(A& x)
{
    /* make x pretty */
    return x;
}
A pretty(A&& x)
{
    /* make x pretty */
    return x;
}

知道我想:

  1. 完全相同的方式修改参数(x(,无论参数是右值引用还是左值引用(实际上,两个部分/* make x pretty */相同(,因此具有单个函数;

  2. 避免不必要的复制;

  3. 能够使用该函数修改变量;

  4. 能够"管道"函数调用,无论参数是右值还是左值。

作为 3. 和 4. 的示例,请考虑以下用例:

void read_A(const A& x) { /* ... */ }
void take_A(A&& x)      { /* ... */ }
A x();
read_A(pretty(x));
take_A(pretty(A()));

我的想法是利用转发引用,同时将允许的参数限制为对A的引用。但是返回类型呢?

template<typename T>
std::enable_if_t<std::is_same<T, A>::value>
/*???*/ pretty(T&& x)
{
    /* make x pretty */
    return x; //?
}

为什么不干脆写

#include <iostream>
template <class T>
T somefunc(T&& a) {
  /* do something with a */
  std::cout << __PRETTY_FUNCTION__ << 'n';
  return std::forward<T>(a);
}
int main(int argc, char* argv[]) {
  int a = 5;
  somefunc(a);
  somefunc(5);
  return 0;
}

哪个会返回

T somefunc(T &&) [T = int &]
T somefunc(T &&) [T = int]

如您所见,这些函数具有您想要的签名。在第一次调用中,T 得到 int & ,因此返回值 int & 。在第二个中,你有 T = int = int && ,这又是你想要的。

顺便说一下您最初考虑应用 std::is_same 来禁用A以外的类型的重载解析似乎也是错误的。根据cpp偏好,

如果 TU 命名具有相同常量易失性限定条件的相同类型,则提供等于 true 的成员常量值。否则,值为 false

因此,您可能希望使用类似 decay ,或者至少是remove_cvremove_reference的组合来应用您想要的逻辑。下面是上述代码的修改版本,现在包括重载解析启用程序。

#include <iostream>
#include <type_traits>
template <class T> T somefunc(T &&a) {
  /* do something with a */
  std::cout << __PRETTY_FUNCTION__ << 'n';
  return std::forward<T>(a);
}
template <class T, typename std::enable_if<
                       std::is_same<typename std::decay<T>::type, float>::value,
                       int>::type = 0>
T onlyfloat_correct(T &&a) {
  /* do something with a */
  std::cout << __PRETTY_FUNCTION__ << 'n';
  return std::forward<T>(a);
}
template <class T,
          typename std::enable_if<std::is_same<T, float>::value, int>::type = 0>
T onlyfloat_wrong(T &&a) {
  /* do something with a */
  std::cout << __PRETTY_FUNCTION__ << 'n';
  return std::forward<T>(a);
}
int main(int argc, char *argv[]) {
  int a = 5;
  const float b = 5;
  somefunc(a);
  somefunc(5);
  onlyfloat_correct(b);
  // onlyfloat_wrong(b);
  return 0;
}

这里根本不需要模板。 您可以利用右值引用是右值引用重载中所有调用pretty的左值这一事实,因此您只需在左值重载中对make x pretty逻辑进行编码。 那看起来像

A& pretty(A& x)
{
    /* make x pretty */
    return x;
}
A pretty(A&& x)
{
    return std::move(pretty(x));
}