c++11新特性——forward和Lambda

简介: c++11新特性——forward和Lambda

一、forward

作用:完美转发,参数在传递过程中,保持原有的属性。即参数原来是左值,则传递之后仍然是左值,若是右值,则传递之后仍然是右值。

在学习forward之前,我们先看一个例子:

int &&a = 5;
// a = 50; // 修改成功
int &&right_ref = a; // 错误

分析:这里a是一个右值引用,指向右值5。 但是,a本身是个左值(如果这里不理解,复习下我之前的文章左值右值)。用右值引用指向左值是不对的。那么,如何保持a自身右值引用的特性呢?forward就是干这个的。

int &&a = 5;
// a = 50; // 修改成功
int &&right_ref = std::forward<int>(a); // ok

下面我们再通过例子来看下:

#include <iostream> 
using namespace std; 
template <class T> 
void Print(T &t) { cout << "L" << t << endl; }
template <class T> 
void Print(T &&t) { cout << "R" << t << endl; }
template <class T> 
void func(T &&t) { 
  Print(t); 
  Print(std::move(t)); 
  Print(std::forward<T>(t)); 
}
int main() { 
  cout << "-- func(1)" << endl; 
  func(1); 
  cout << "-- func(x)" << endl; 
  int x = 10; 
  func(x); // x本身是左值 
  cout << "-- func(std::forward<int>(y))" << endl; 
  int y = 20; 
  func(std::forward<int>(y)); // 
  return 0; 
}

运行结果:

![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/1462219cabb54488ae735871b91ad771.png

二、匿名函数Lambda

2.1 定义

lambda表达式可以看成是一般函数的函数名被略去,返回值使用了一个 -> 的形式表示。唯一与普通函数不同的是增加了“捕获列表”。

2.2 语法格式
int main() { 
  [捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 { 
    // 函数体 
  }
  auto Add = [](int a, int b)->int { return a + b; };
  std::cout << Add(1, 2) << std::endl; //输出3 
  return 0; 
}
2.3 捕获列表

有时候需要在匿名函数体内部使用外部变量,lambda表达式使用捕获列表来传递参数,根据传参的行为,分为:

2.3.1 值捕获

与参数传递类似,值捕获的前提是变量可以拷贝,不同点是,拷贝发生在lambda表达式被创建时,而非调用时。匿名函数体内部不能修改外部变量的值。

void func() { 
  cout << "func" << endl; 
  int c = 12; 
  int d = 30; 
  // 创建时就已经把c、d的值拷贝进去了,所以输出:d = 30;
  auto Add = [c, d](int a, int b)->int { 
    // c = 100; // 编译报错
    cout << "d = " << d << endl; 
    return c; 
  };
  d = 20; 
  std::cout << Add(1, 2) << std::endl; 
}

2.3.2 引用捕获

匿名函数体内部可以修改外部变量。

void func() { 
  cout << "func" << endl; 
  int c = 12; 
  int d = 30; 
  // 创建时就已经把c、d的值拷贝进去了
  auto Add = [&c, &d](int a, int b)->int { 
    c = 100; // 编译ok
    cout << "d = " << d << endl; 
    return c; 
  };
  d = 20; // 因为是引用捕获,且这里修改了d的值,所以输出:20
  std::cout << Add(1, 2) << std::endl; 
}

2.3.3 隐式捕获

手动书写捕获列表,有时候会很复杂。我们可以交给编译器去完成,在捕获列表中写一个 & 或者 = ,向编译器声明是引用捕获还是值捕获。

auto Add = [&](int a, int b)->int { 
    c = 100; // 编译ok
    cout << "d = " << d << endl; 
    return c; 
  };

2.3.4 [] 空捕获列表

表明不能使用外部变量。

auto Add = [](int a, int b)->int { 
    c = 100; // 编译报错
    cout << "d = " << d << endl; 
    return c; // 编译报错
  }; 

2.3.5 表达式捕获

上面4种,值和引用捕获都是捕获的已经在外部作用域声明的变量,因此这些捕获的方式均为左值。c++14之后,支持捕获右值,允许捕获的成员用任意的表达式进行初始化。

// C++14 支持表达式捕获
void func() { 
  cout << "func" << endl; 
  auto important = std::make_unique<int>(1); 
  auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int {     
    return x + y + v1 + (*v2); 
  };
  std::cout << add(3,4) << std::endl;
}

2.3.6 泛型Lambda

在c++14之前,Lambda表达式的形参必须执行具体的类型。从c++14开始,支持auto泛型形参。

//泛型 Lambda C++14 
void func() { 
  cout << "func" << endl; 
  auto add = [](auto x, auto y) { return x+y; };
  std::cout << add(1, 2) << std::endl; 
  std::cout << add(1.1, 1.2) << std::endl; 
}

2.3.7 可变Lambda

  • 采用值捕获的方式,lambda不能修改其值,如果想要修改,使用mutable修饰;
  • 采用引用捕获的方式,lambda可以直接修改其值

文章参考与<零声教育>的C/C++linux服务期高级架构


相关文章
|
27天前
|
编译器 程序员 定位技术
C++ 20新特性之Concepts
在C++ 20之前,我们在编写泛型代码时,模板参数的约束往往通过复杂的SFINAE(Substitution Failure Is Not An Error)策略或繁琐的Traits类来实现。这不仅难以阅读,也非常容易出错,导致很多程序员在提及泛型编程时,总是心有余悸、脊背发凉。 在没有引入Concepts之前,我们只能依靠经验和技巧来解读编译器给出的错误信息,很容易陷入“类型迷路”。这就好比在没有GPS导航的年代,我们依靠复杂的地图和模糊的方向指示去一个陌生的地点,很容易迷路。而Concepts的引入,就像是给C++的模板系统安装了一个GPS导航仪
100 59
|
1月前
|
存储 编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(三)
【C++】面向对象编程的三大特性:深入解析多态机制
|
1月前
|
存储 编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(二)
【C++】面向对象编程的三大特性:深入解析多态机制
|
1月前
|
编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(一)
【C++】面向对象编程的三大特性:深入解析多态机制
|
1月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
20天前
|
C++
C++ 20新特性之结构化绑定
在C++ 20出现之前,当我们需要访问一个结构体或类的多个成员时,通常使用.或->操作符。对于复杂的数据结构,这种访问方式往往会显得冗长,也难以理解。C++ 20中引入的结构化绑定允许我们直接从一个聚合类型(比如:tuple、struct、class等)中提取出多个成员,并为它们分别命名。这一特性大大简化了对复杂数据结构的访问方式,使代码更加清晰、易读。
31 0
|
1月前
|
存储 编译器 C++
【C++】面向对象编程的三大特性:深入解析继承机制(三)
【C++】面向对象编程的三大特性:深入解析继承机制
|
1月前
|
编译器 C++
【C++】面向对象编程的三大特性:深入解析继承机制(二)
【C++】面向对象编程的三大特性:深入解析继承机制
|
1月前
|
安全 程序员 编译器
【C++】面向对象编程的三大特性:深入解析继承机制(一)
【C++】面向对象编程的三大特性:深入解析继承机制
|
1月前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值