13

正如我们所知,在最常见的情况下,T&&意味着“这是一个临时对象”。但是,如果想要从函数中返回一个临时对象,他/她可以如下声明该函数:

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

    return t;
}

或(注:不正确)

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

    return t;
}

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

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

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

4

3 回答 3

13

在您的示例中,这T&&是错误的,它是一个悬空的参考。

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

仅当您希望函数的调用者能够从该引用所指的任何内容中移动时,您才应该返回一个右值引用。

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

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

于 2013-01-30T14:01:22.247 回答
4

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使用 a 调用并将其T&强制转换为T&&,其中T明确说明。因此,如果原始函数参数是左值引用,它将返回一个,否则将返回一个右值引用,从而实现完美转发。

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

template<typename T> T&& foo(T&& bar) {/*some ops*/ return std::forward<T>(bar);}
于 2013-01-30T13:55:53.427 回答
2

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

非模板

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

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

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

模板

IfT是一个模板参数,T&&是 Scott Meyers 所说的Universal Reference。这意味着T&&将使用引用折叠来确定的类型...简而言之,如果i 不是引用类型,则表示它T&&是一个右值,如果它是一个左值引用。T

于 2013-01-30T13:55:37.920 回答