通过引用参考文献直观地理解函数[重复]

2024-03-01

可能的重复:

由于某种原因,这超出了我的直觉,我在互联网上找不到任何解释。 C++ 函数获取引用的引用意味着什么?例如:

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

我理解通过引用传递的想法,所以

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

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

    return 0;
}

对我来说有效且直观。


这不是参考的参考,而是一个新的语言功能,称为右值引用 //www.artima.com/cppsource/rvalue.html它(非正式地)表示对内存中对象的引用,该对象在程序的其他地方未引用并且可以被破坏性修改。例如,函数的返回值可以通过右值引用捕获,就像引入到表达式中的临时值一样。

右值引用可用于多种目的。从大多数C++程序员的角度来看,它们可以用来实现移动语义 //www.stroustrup.com/C++11FAQ.html#rval,由此可以通过将旧对象的内容从旧对象“移出”到新对象中来初始化新对象。您可以使用它从 C++11 中的函数返回巨大的对象,而无需花费巨大的成本来复制该对象,因为用于捕获返回值的对象可以使用移动构造函数进行初始化,只需从临时对象中窃取内部结构由 return 语句创建。

移动语义与复制语义正交,因此对象可以移动但不可复制。例如,std::ofstreams 不可复制,但它们是可移动的,因此您可以返回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. 仍然让该对象被分配一个新值。后一点很棘手,但重要的是确保您仍然可以在某个时刻为清除的对象赋予新值。这是因为可以获得对对象的右值引用,该对象稍后仍可以在程序中引用。

为了阐明 (2),右值引用的一个有趣的用例是能够在对象之间显式移动值。例如,考虑这个惯用的实现swap:

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

这段代码是合法的,但有点不寻常。特别是,它最终会制作三份副本 - 首先是在设置时temp等于的副本lhs,一旦设定lhs成为的副本rhs,并且一旦设置rhs成为的副本temp。但我们真的不想在这里制作任何副本;相反,我们只想打乱这些值。因此,在 C++11 中,您将能够通过使用显式获取对象的右值引用std::move功能:

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

现在,根本没有复制。我们移动的内容lhs into temp,然后移动内容rhs into lhs,然后移动内容temp into rhs。在这样做的过程中,我们离开了lhs and rhs在将新值放入其中之前暂时处于“清空”状态。重要的是,在编写将内容移出对象的代码时,我们要使对象处于某种格式良好的状态,以便该代码正常工作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过引用参考文献直观地理解函数[重复] 的相关文章

随机推荐