Google C++ Coding Style:右值引用(Rvalue Reference)

右值引用是一个C++11特性,标记为T&&。GSG中定义:只为移动建构函数(Move constructor)和移动赋值操作(Move assignment)使用右值引用。并且不要使用std::Forward(提供的完美转发特性)。

C++中右值指表达式结束时就不再存的临时对象。在C++11中,右值分为纯右值(即原始字面量,表达式产生的临时变量等),以及一个将亡值(expiring value, 使用<<深入应用C++11>>中的译法,指的是与右值引用相关的表达式,如将被移动的对象,T&&函数返回值等)。

以函数返回值表达不出右值引用的威力,因为编译的本身的优化会解决不必要的对象复制操作。而作为函数参数,如果使用const T&之类的形式也能够有效避免不必要的对象拷贝。这里特别以与标准容器配合,体现一下,右值引用最大的价值:避免深拷贝。

// 下面一个完整提供了Move Constructor和Move assignment的类。
#include <iostream>
#include <string>
#include <vector>
#include <string.h>

class Foo {
 private:
    int x = 0;
    int y = 0;
    char* strPtr = nullptr;
 public:
    Foo() {
        std::cout << "Constructor was called." << std::endl;
    }

    Foo(const char* s) {
        std::cout << "Constructor with string:" << s << std::endl;
        if (s != nullptr) {
          strPtr = new char[strlen(s)];
          strcpy(strPtr, s);
        }
    }

    // Copy constructor
    Foo(const Foo& a) : x(a.x),
                        y(a.y) {
        // Deep copy
        copyStringValue(a.strPtr);
        std::cout << "Copy constructor was called." << std::endl;
    }

    // Move constructor, no need copy string in deep.
    Foo(Foo&& a) : x(a.x),
                   y(a.y),
                   strPtr(a.strPtr) {
        a.strPtr = nullptr;  // 注意要清掉之前的字串,这样才是移动。
        std::cout << "Move constructor was called." << std::endl;
    }

    Foo& operator=(const Foo& a) {
        x = a.x;
        y = a.y;
        copyStringValue(a.strPtr);
        std::cout << "Assignment Operator was called." << std::endl;
    }

    ~Foo() {
        if (strPtr != nullptr) {
            std::cout << "Free allocated string:" << strPtr << std::endl;
            delete strPtr;
        }
        std:: cout << "Deconstructor was called." << std::endl;
    }

 private:
    void copyStringValue(const char* s) {
        if (strPtr != nullptr) {
            delete strPtr;
            strPtr = nullptr;
        }

        if (s != nullptr) {
            strPtr = new char[strlen(s)];
            strcpy(strPtr, s);
        }
    }
};

int main(void) {
    {
      std::cout << "Need to clear string twice:" << std::endl;
      std::vector<Foo> myVec;
      Foo a("Instance A");
      myVec.push_back(a);
    }

    std::cout << "============" << std::endl;

    {
      std::cout << "Only need to clear string one time:" << std::endl;
      std::vector<Foo> myVec;
      Foo c("Instance C");
      myVec.push_back(std::move(c));
    }

    std::cout << "============" << std::endl;
    {
        Foo d("Instance D");
        Foo x = d;
    }

    std::cout << "============" << std::endl;
    {
        Foo e("Instance E");
        Foo&& y = std::move(e);
    }
}

观察代码删除字串的次数,就可以了解右值引用的作用了。程序运行的输出如下:

Need to clear string twice:
Constructor with string:Instance A
Copy constructor was called.
Free allocated string:Instance A
Deconstructor was called.
Free allocated string:Instance A
Deconstructor was called.
============
Only need to clear string one time:
Constructor with string:Instance C
Move constructor was called.
Deconstructor was called.
Free allocated string:Instance C
Deconstructor was called.
============
Constructor with string:Instance D
Copy constructor was called.
Free allocated string:Instance D
Deconstructor was called.
Free allocated string:Instance D
Deconstructor was called.
============
Constructor with string:Instance E
Free allocated string:Instance E
Deconstructor was called.

但是考虑到右值引用中的引用折叠(reference collapsing)会引入一些复杂度(左右值的转换规则),造成理解上的问题,所以将右值引用的应用范围做了如开篇所说的限定。

在实际应用中,会出现没有直接定义类型的右值引用,被称为universal reference,需要进行类型推导。另一种情况是使用auto &&定义的也是universal reference。

关于std::forward,它被称为完美转发(Perfect Forwarding)。要解决的问题是在函数模板中,完全依照模板的参数的类型,保持参数的左值,右值特征),将参数传递给函数模板中调用的另一个函数(转自<<深入应用C++11>>)。根据这个定义,完美转发仅针对需要调用内部实现的模板函数,而且需要开发者识别出哪些情况是有效的,而哪些情况下又是无效的。比如适用的场景:

template<class T>
void foo(T&& arg)
{
  // 如下保持arg的类型传入到bar()中
  bar(std::forward<T>(arg));
}

但如果内部函数无需要针对左值或右值做特殊处理,这种场景是不需要转发的。参考:When not to use std::forward

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-03 22:13:10

Google C++ Coding Style:右值引用(Rvalue Reference)的相关文章

[转][c++11]我理解的右值引用、移动语义和完美转发

c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的持久化对象,右值是指表达式结束时就不再存在的临时对象.所有的具名变量或者对象都是左值,而右值不具名.很难得到左值和右值的真正定义,但是有一个可以区分左值和右值的便捷方法:看能不能对表达式取地址,如果能,则为左值,否则为右值. 看见书上又将右值分为将亡值和纯右值.纯右值就是c++98标准中右值的概念,

[c++11]右值引用、移动语义和完美转发

c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的持久化对象,右值是指表达式结束时就不再存在的临时对象.所有的具名变量或者对象都是左值,而右值不具名.很难得到左值和右值的真正定义,但是有一个可以区分左值和右值的便捷方法:看能不能对表达式取地址,如果能,则为左值,否则为右值. 看见书上又将右值分为:将亡值和纯右值. 纯右值就是c++98标准中右值的概

[C++]右值引用和转移语义

右值引用和转移语义 本文尝试着解释何为右值引用和转移语义以及使用它们具有优势,并提供相关案例分析. 定义 左值和右值 首先我们先来理解一下什么是左值和右值. C/C++语言中可以放在赋值符号左边的变量,左值表示存储在计算机内存的对象,左值相当于地址值.右值:当一个符号或者常量放在操作符右边的时候,计算机就读取他们的"右值",也就是其代表的真实值,右值相当于数据值. C/C++语言中可以放在赋值符号左边的变量,即具有对应的可以由用户访问的存储单元,并且能够由用户去改变其值的量.左值表示存

C++11 标准新特性: 右值引用与转移语义

C++ 的新标准 C++11 已经发布一段时间了.本文介绍了新标准中的一个特性,右值引用和转移语义.这个特性能够使代码更加简洁高效. 查看本系列更多内容 | 3 评论: 李 胜利, 高级开发工程师, IBM 2013 年 7 月 10 日 内容 在 IBM Bluemix 云平台上开发并部署您的下一个应用. 开始您的试用 新特性的目的 右值引用 (Rvalue Referene) 是 C++ 新标准 (C++11, 11 代表 2011 年 ) 中引入的新特性 , 它实现了转移语义 (Move

c++11 右值引用与转移语义

右值引用 (Rvalue Referene) 是 C++ 新标准 (C++11, 11 代表 2011 年 ) 中引入的新特性 , 它实现了转移语义 (Move Sementics) 和精确传递 (Perfect Forwarding).它的主要目的有两个方面: 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率. 能够更简洁明确地定义泛型函数. 左值与右值的定义 C++( 包括 C) 中所有的表达式和变量要么是左值,要么是右值.通俗的左值的定义就是非临时对象,那些可以在多条语句中使

左值、右值、右值引用与move()、forward()

1.左值(lvalue):可以进行取地址(&)运算的是左值.或者有时候可以理解为 既能够出现在等号左边也能出现在等号右边的变量(或表达式). 2.右值(rvalue):不可以进行取地址(&)运算的是右值.或者有时候可以理解为 只能出现在等号右边的变量(或表达式). 常见的右值有 字面量.函数返回的临时对象,匿名对象等. 以上判断一个对象是左值还是右值并不完全正确. const int c_a = 10; //左值,但是不能被赋值,也就不能出现在 = 左边 //字符串字面值,可以取地址,是左

Google C++ Coding Style:引用参数

Google C++ Coding Style定义 输入参数以值或者const引用形式传入,输出参数使用指针. 所有以引用形式输入参数必须加上const,即const T&的形式. 即如下形式: void Foo(const string &in, string *out); 在如下情况下, 可以使用const T*的形式: * 需要进行指针的判空 (即空指针是合理的). * 需要使用到输入参数的指针或引用形式. 为什么要使用const T&形式? 以值传入是最为安全的形式,因为它

翻译「C++ Rvalue References Explained」C++右值引用详解 Part3:右值引用

本文为第三部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/4220233.html. 右值引用 如果x是任意类型,那么x&&则被称作一个对x的右值引用(rvalue reference).为了更好区分,原来的引用x&现在也被称作左值引用(lvalue reference). 一个右值引用是一种同原始引用x&的行为非常类似的类型,但是有一些特例.最重要的一个就是当面临方法重载决议的时候,左值倾向于旧式的左值引用,而右值偏向于新的

C++11标准之右值引用(rvalue reference)

1.右值引用引入的背景 临时对象的产生和拷贝所带来的效率折损,一直是C++所为人诟病的问题.但是C++标准允许编译器对于临时对象的产生具有完全的自由度,从而发展出了Copy Elision.RVO(包括NRVO)等编译器优化技术,它们可以防止某些情况下临时对象产生和拷贝.下面简单地介绍一下Copy Elision.RVO,对此不感兴趣的可以直接跳过: (1) Copy Elision Copy Elision技术是为了防止某些不必要的临时对象产生和拷贝,例如: struct A { A(int)