何时返回T&&来自非模板和非成员函数

时间:2016-01-16 21:53:18

标签: c++ c++11 c++14

请注意,有关模板函数或成员函数的先前答案,但此问题仅与非模板非成员函数有关。 std :: move()返回T&&,但它是一个模板函数。

是否有充分的理由使用T&&作为非模板和非成员函数的返回类型,其中T是任意类型?

例如,您何时会使用以下内容?

T&& fn()
{
....
return ....
}

我已经看过使用过这个例子的例子,但是在所有的例子中,开发人员误解了移动语义,应该按价值返回并利用NRVO。

2 个答案:

答案 0 :(得分:0)

假设我们有一个粒子系统。我们希望将其实现为粒子池。这意味着我们要反复回收相同的粒子,因此为了重用资源,我们希望传递rvalues。

现在,假设我们的粒子具有非常短的生命周期,但我们希望在它们“到期”时发生某些事情(比如递增整数x)但我们仍然想要回收它们。现在,假设我们希望能够指定x。但是,现在我们做什么了?

在移动中,我们希望能够调用函数来增加变量,但该变量必须波动。我们不希望将它放入析构函数中,因为这将涉及在编译时派生精确函数调用的模板,或者我们需要一个std::function或函数指针来引用粒子内部的函数,浪费空间。我们想要做的是能够接受到期值,做一个动作,然后转发它。 换句话说,我们想要一个具有副作用的传出移动,特别是从左值到右值的转换。

您执行此操作的重要性 - 左值转换左值或使用operator=operator()接收到另一个粒子时。你可以在一个对象接收到一个值时或者当你使用rvalue时这样做。但是假设我们想为许多不同类型的对象做这个 - 你想为5个不同的类中的每一个写5个或更多不同的移动函数吗? ,或者我们应该使用外部模板化函数对类型进行参数化?

你还怎么做?你仍然会在对象内部使用std::move(),但我们希望将它与对象外部的函数耦合,因为这会代表移动的副作用。

Coliru:http://coliru.stacked-crooked.com/a/0ff11890c16f1621

#include <iostream>
#include <string>

struct Particle {
    int i;    
};

template <typename T>
T&& move( T& obj,  int (*fn)(int) , int& i ) {
    i = fn(i);
    return(std::move(obj));
}

int increment(int i) { return i+1; }

int main() {
    // Have some object pool we want to optimize by moving instead of creating new ones.
    // We'll use rvalue semantics so we can "inherit" lifetime instead of copying.
    Particle pool[2000];

    // Fill up the "particles".
    for (auto i = 0; i < 2000; ++i) {
        pool[i].i = i;    
    }

    // Perform the moves with side effects.
    int j = 0;
    for (auto i = 0; i < 1999; ++i) {
        pool[i+1] = move<Particle>(pool[i], &increment, j);
        std::cout << "Moves performed: " << j << '\n';
    }

}

答案 1 :(得分:-1)

有时,使用一个返回*this作为l值引用的方法会很方便,这样就可以将临时函数传递给接受l值引用的函数。

func(foo_t().lval_ref());

那么,人们为什么要这样做呢?假设一个函数将l值引用作为输出变量。如果调用者实际上不需要这个输出,那么传递临时代替而不是定义一些虚拟变量unused,然后static_cast<void>(unused);以禁止任何未使用变量的警告将是方便的。

类似地,可能希望有一个方法返回*this作为r值引用,其使用方式与std::move()相同。这样做可以提供更大的灵活性。你可以在方法实现中做任何你想要的棘手的事情: - )