直观理解参考引用的函数

时间:2011-08-22 21:46:07

标签: c++ pass-by-reference rvalue-reference c++11

  

可能重复:
  What does T&& mean in C++11?

出于某种原因,这是在逃避我的直觉,我在互联网上找不到任何解释。 C ++函数引用引用是什么意思?例如:

void myFunction(int&& val);     //what does this mean?!

我理解传递参考的想法,所以

void addTwo(int& a)
{
    a += 2;
}

int main()
{
    int x = 5;
    addTwo(x);

    return 0;
}

对我来说很直观。

2 个答案:

答案 0 :(得分:32)

这不是引用的引用,而是一种称为rvalue reference的新语言特性,它(非正式地)表示对内存中对象的引用,该引用未在程序中的其他地方引用,并且可以是破坏性的改性。例如,函数的返回值可以通过右值引用捕获,引入表达式的临时值也可以捕获。

Rvalue参考可用于各种目的。从大多数C ++程序员的角度来看,它们可用于实现move semantics,从而可以通过将旧对象的内容“移动”出旧对象并进入新对象来初始化新对象。您可以使用它来从C ++ 11中的函数返回大型对象,而无需花费巨大的成本来复制对象,因为用于捕获返回值的对象可以使用移动构造函数通过从临时对象中窃取内部来初始化由return语句创建。

移动语义与复制语义正交,因此对象可以移动而不可复制。例如,std::ofstream s不可复制,但它们是可移动的,因此您可以使用移动行为从函数返回std::ofstream。目前无法在C ++ 03中完成。例如,这段代码在C ++ 03中是非法的,但在C ++ 11中非常好(并且鼓励!)

std::ifstream GetUserFile() {
    while (true) {
        std::cout << "Enter filename: ";
        std::string filename;
        std::getline(std::cin, filename);

        ifstream input(filename); // Note: No .c_str() either!
        if (input) return input;

        std::cout << "Sorry, I couldn't open that file." << std::endl;
    }
}

std::ifstream file = GetUserFile(); // Okay, move stream out of the function.

直观地说,采用右值引用的函数是(可能)通过将旧对象的内容移动到新对象中来试图避免昂贵的副本的函数。例如,您可以通过使构造函数接受右值引用来为类似向量的对象定义移动构造函数。如果我们将向量表示为指向数组的指针的三元组,数组的容量和已用空间,我们可以按如下方式实现其移动构造函数:

vector::vector(vector&& rhs) {
    /* Steal resources from rhs. */
    elems    = rhs.elems;
    size     = rhs.size;
    capacity = rhs.capacity;

    /* Destructively modify rhs to avoid having two objects sharing 
     * an underlying array.
     */
    rhs.elems    = nullptr; // Note use of nullptr instead of NULL
    rhs.size     = 0;
    rhs.capacity = 0;
}

重要的是要注意,当我们在构造函数的末尾清除rhs时,我们最终会将rhs置于这样的状态

  1. 析构函数调用时不会导致崩溃(请注意我们将其元素指针设置为nullptr,因为释放nullptr是安全的),并且
  2. 仍然可以为对象分配新值。后一点很棘手,但重要的是要确保在某些时候仍然可以为清除的对象提供新值。这是因为可以获得对象的右值引用,该对象仍可在程序中稍后引用。
  3. 为了阐明(2),rvalue引用的一个有趣用例是在对象之间显式移动值的能力。例如,请考虑swap

    的惯用实现
    template <typename T> void swap(T& lhs, T& rhs) {
        T temp = lhs;
        lhs = rhs;
        rhs = temp;
    }
    

    此代码合法,但有点不寻常。特别是,它最终会制作三份副本 - 首先将temp设置为等同lhs的副本,将lhs设置为rhs的副本,然后设置一次rhstemp的副本。但我们真的不想在这里制作任何副本;相反,我们只是想改变周围的价值观。因此,在C ++ 11中,您将能够使用std::move函数显式获取对象的rvalue引用:

    template <typename T> void swap(T& lhs, T& rhs) {
        T temp = std::move(lhs);
        lhs = std::move(rhs);
        rhs = std::move(temp);
    }
    

    现在,根本没有制作副本。我们将lhs的内容移至temp,然后将rhs的内容移至lhs,然后将temp的内容移至rhs 。这样做,我们暂时将lhsrhs置于“清空”状态,然后再将新值放入其中。重要的是,在编写代码时,要将内容移出一个对象,我们将对象保持在一个格式良好的状态,以便此代码正常工作。

    希望这有帮助!

答案 1 :(得分:2)

这不是对参考的引用。它是C ++ 0x中为所谓的Rvalue references引入的新语法。