将函数的返回类型声明为 T&&& 是否毫无意义?

Is it meaningless to declare the return type of a function as T&&?

本文关键字:是否 毫无意义 函数 返回类型 声明      更新时间:2023-10-16

正如我们所知,在大多数情况下,T&&的意思是"这是一个临时对象"。但是,如果要从函数返回临时对象,他/她可以按如下方式声明该函数:

template<class T>
T f()
{
    T t;
    ......
    return t;
}

或(注意:不正确)

template<class T>
T&& f()
{
    T t;
    ......
    return t;
}

但我认为后者过头了,因为前者已经足够了,而且向后兼容。

然而,我也发现std::forward()的返回类型被声明为 T&&&,所以我确信我对此的理解是不完整的。

我真正的问题是:我们应该在何时何地将函数的返回类型声明为 T&&?

在你的例子中,T&&是错误的,它是一个悬而未决的引用。

但是std::forward不会在其自己的定义中返回对局部变量的右值引用,而是返回对其按值引用参数的右值引用(或对按值引用参数的左值引用)。

仅当希望函数的调用方能够从该引用的任何内容移动时,才应返回右值引用。

通常,只有当函数的目的是提供对某个重要对象(可能已经存在)的移动访问时,才会这样做。因此,这包括std::move(它允许您从左值移动),类似地,您可以编写一个专门为用户设计的访问器函数,以便从某个对象的数据成员或某个容器的元素移动。如果对象本身不重要,只有值,则可以按值返回。

正如 grizzly 所说,有时由于引用折叠,您可以利用技巧,这意味着您在代码中键入T&&,但是当T已经是左值引用类型时T&&是相同的左值引用类型。 std::forward使用了这个技巧。也就是说,由于引用折叠T&&并不意味着"对T的右值引用",它意味着"如果T是引用类型,则为T,否则对T的右值引用"。

T&&

不一定意味着结果是右值。与模板参数一起使用时&&表示通用引用,该引用可以是右值或左值引用。更具体地说:如果T是左值引用类型foo&T&&实际上是对foo的左值引用,否则表示右值引用。

这可用于编写函数,这些函数接受任何类型的参数:

template<typename T> void foo(T&&);
bar a;
const bar b;
foo(a);//T and T&& will both be bar&
foo(b);//T and T&& will both be const bar&
foo(bar());//T will be bar, T&& will be bar&&

考虑到这一点std::forward用一个T&调用它并将其强制转换为T&&,其中明确陈述了T。因此,如果原始函数参数是左值引用,它将返回一个,否则它将返回一个右值引用,从而实现完美转发。

至于何时将其用作返回类型: 很少,尽管在传递参数时避免复制可能很有用,例如:

template<typename T> T&& foo(T&& bar) {/*some ops*/ return std::forward<T>(bar);}

答案取决于您的函数是否是模板函数。在你的问题中是,但让我们先看看它是否不是:

非模板

T&&作为返回类型并非毫无意义。原因是它实际上并不意味着"临时对象"。它的意思是"右值引用"。差异是微妙的,但可以使用与此返回类型相关的一类函数突出显示。也就是说,如果我们想返回对右值的引用,但它引用的对象不是我们函数的本地对象怎么办?

T&& return_rvalue(/*some data*/)
{
   T&& t = Get_a_reference();
   // Do something fascinating.
   return static_cast<T&&>(t);
}

这种模式的一个非常特殊的情况是函数std::move,它接受任何引用并返回相应的右值引用。当然,在实际代码中,您当然应该使用std::move而不是直接执行强制转换,因为这更清楚地表明了您的意图。

模板

如果T是模板参数,则T&& Scott Meyers 所说的通用引用。这意味着T&&的类型将使用参考折叠来计算出来......简而言之,这意味着如果 T i 不是引用类型,则T&&是右值,如果是,则为左值引用。