相关推荐recommended
15、lambda表达式、右值引用、移动语义
作者:mmseoamin日期:2023-12-14

前言

返回值后置

auto 函数名 (形参表) ->decltype(表达式)

lambda表达式

lambda表达式的名称是一个表达式 (外观类似函数),但本质绝非如此

语法规则

[捕获表] (参数表) 选项 -> 返回类型
{
函数体;
}

lambda表达式的本质

  • lambda表达式本质其实是一个类
  • 并且最终返回值为这个类的对象
  • 因此对lambda表达式的调用就是该对象的函数操作符的调用

    简写

    • 可以没有返回值类型,将根据return推断
    • 如果连return也没有,则返回值为void
    • 参数为void可以省略不写

      捕获表

      • []:不捕获任何外部变量
      • [variable] : 捕获外部变量的值(具备只读属性)
      • [&variable]: 按引用捕获,指定的外部变量
      • [this]: 捕获this指针,访问外部对象的成员
      • [=]: 按值捕获所有的外部变量,也包括this
      • [&]: 按引用捕获所有的外部变量,也包括this
      • [=,&variable]: 按值捕获所有的外部变量包括this,但是指定的外部变量按引用捕获
      • [&,=variable]: 按引用捕获所有的外部变量,也包括 this,但是指定的外部变量按值捕获
        // lambda表达式
        #include 
        #include 
        using namespace std;
        int Max(int x, int y){
            return x > y ? x : y;
        }
        int main( void ){
            int a = 10, b = 20;
            cout << Max(a,b) << endl;;
            
            auto f = [](int x, int y)->int{ return x > y ?  x : y; };
            // 编译器根据lambda表达式(1)生成一个类 (2)类内定义函数操作符函数 (3)返回这个类的匿名对象
            /*
                class Z4mainEUliiE_{
                public:
                     int operator()(int x, int y){
                          return x > y ?  x : y;
                     }
                };
                auto f = Z4mainEUliiE_{};
            */
            cout << "f的类型:" << typeid(f).name() << endl;
            cout << f(a,b) << endl; // f.operator()(a,b)
            // lambda表达式可以没有返回值类型,根据return判断
            cout << [](int x, int y) { return x+y; }(a,b) << endl;
            /*
                 class X{
                 public:
                     auto operator()(int x, int y)->decltype(x+y){
                        return x + y;
                     }
                 };
                 cout << X{}(a,b) << endl; // cout << X{}.operator()(a,b) << endl;
            */ 
            // lambda表达式可以没有返回类型,也没有retrun语句,返回类型为void
            [](int x, int y){ cout << x << ' ' << y << endl; }(a,b);
            /*
               class XX{
               public:
                    void operator()(int x, int y){
                        cout << x << ' ' << y << endl;
                    }
               };
               XX{}(a,b); // XX{}.operator()(a,b)
            */
            // 如果没有形参,可以省略不写
            []{ cout << "无聊" << endl;}();
            /*
                 class XXXX{
                 public:
                      void operator(){
                        cout << "无聊" << endl;
                      } 
                 };
                 XXXX{}();  // XXXX().operator()()
             */ 
            return 0; 
        } 
        
        // lambda表达式 -- 捕获表(捕获lambda表达式外部的变量信息)
        #include 
        #include 
        using namespace std;
        int a = 10;
        class Y{
        public:
            void foo(/* Y* this */ int c = 30 ){
                cout << "-------------[]----------------" << endl;
                [](int d = 40){
                    cout << "a=" << a << endl;
                    cout << "b=" << b << endl;
        //          cout << "c=" << c << endl; // error
                    cout << "d=" << d << endl;
        //          cout << "e=" << e << endl; // error
                }();
                /*
                    class X{
                    public:
                        void operator()(int d = 40)){
                             cout << "a=" << a << endl;
                             cout << "b=" << b << endl;
                         //  cout << "c=" << c << endl; // error
                             cout << "d=" << d << endl;
                         //  cout << "e=" << this->e << endl; // error
                        }
                    };
                    X{}();
                 */
                cout << "-------------[c]----------------" << endl;
                // 捕获外部变量的值
                [c](int d = 40){ cout << "c=" << /*++*/c << endl; }();
                 /* 
                    class XX{
                    public:
                        XX(int m):c(m){} //这里的c并不是foo函数的形参,而是XX类的一个成员变量
                        void operator()(int d = 40){ 
                            cout << "c=" << c << endl; // //这里的c并不是foo函数的形参,而是XX类的一个成员变量
                        }
                    private:
                        const int c; //这里的c并不是foo函数的形参,而是XX类的一个成员变量
                    };
                    XX{c}(); // 这里的c是foo函数的形参c   XX(c).operator()()
                 */
                cout << "-------------[&c]----------------" << endl;
                [&c](int d = 40){ cout << "c=" << ++c << endl; }();
                cout << "-------------[&c]----------------" << endl;
                [this](int d = 40){ cout << "e=" << e << endl; }();
            }
        private:
            static int b;
            int e;
        };
        int Y::b = 20;
        int main( void ){
            Y y;
            y.foo();
            return 0; 
        } 
        

        右值引用

        左值 和 右值

        • 可以“取”地址的值就是左值,左值通常具名
        • 不可“取”地址的值就是右值,右值通常匿名

          15、lambda表达式、右值引用、移动语义,在这里插入图片描述,第1张

          左值引用 和 右值引用

          • 左值引用只能引用左值,不能引用右值
            int a;
            int& b = a; // OK
            int c;
            int& d = a + c; // ERROR
            
            • 右值引用只能引用右值,不能引用左值
              int&& e = a + c;// OK
              int&& f = a; // ERROR
              
              • 常左值引用,既能引用左值,也能引用右值
                const int& g = a + c; // OK
                const int& h = a; // OK
                

                没有必要有常右值引用,因为常右值引用,完全可以被常左值引用替代

                // 左值/右值    左值引用/右值引用
                #include 
                using namespace std;
                int foo( ) {
                    int m=888;
                    return m;
                }
                int main( void ) {
                // 当前作用域的生命期
                // 具名内存-->能够取址-->左值|非常左值(无const修饰)
                //                           |常左值  (有const修饰)
                    int a = 10;
                    int& ra = a; // ok
                    const int& cra = a; // ok
                    const int b = 10;
                //  int& rb = b; // error
                    const int& crb = b; // ok
                // 语句级生命期(引用可以延长右值的生命期)
                // 匿名内存-->不能取址-->右值|直接更改右值毫无意义(98/03标准给出结论)
                //                           | 11标准认为给了真名就可以改
                    const int& ri = 10; 
                    int&& rri  = 10; 
                    const int& rf = /*|888|*/foo( ); // (1)分配一块内存空间  (2)生成跳转指令
                    int&& rrf = foo();
                    return 0;
                }
                
                //左值引用/右值引用
                #include 
                using namespace std;
                int main( void ) {
                    int a,c;
                    // 左值引用只能引用左值,不能引用右值
                    int& b = a;  // ok
                //  int& d = a + c; // error
                   
                    // 右值引用只能引用右值,不能引用左值
                    int&& e = a + c; // ok
                    e = 666;         // ok 通过右值引用不会丧失修改目标内存的权限
                //  int&& f = a;     // error
                  
                    // 常左值引用(万能引用),既能引用左值,也能引用右值
                    const int& g = a;     // ok
                    const int& h = a + c; // ok
                //  g = 666;  // error 但是通过常左值引用会丧失修改目标内存的权限
                    return 0;
                }
                

                移动语义

                资源的转移 代替 资源的重建

                保证功能正确的情况下,做到性能提升