15、【C++】C++11新特性:Lamda表达式/可变参数模板

一、Lamda表达式

    Lamda表达式是C++11中引入的一项新技术,利用Lamda表达式可以编写内嵌的匿名函数,用以替换独立函数或者函数对象,并且使得代码更可读。是一种匿名函数,即没有函数名的函数;Lamda函数的语法定义如下:

[capture] :捕捉列表,捕捉列表总是作为lambda的开始,即出现于lambda的开始处。它是lambda的引出符(即开始标志)。编译器可以根据该“标志”来作出判断出该函数是否为lambda函数。同时“捕捉列表”能够捕捉上下文中的变量以作为lambda函数使用。

(parameters):参数列表。和C/C++中的普通函数参数意义一样。该部分是可选的,意味着如果我们不需要进行参数传递时,可以连同括号“()”一起省略掉

mutable:该关键字为一个修饰符。在默认的情况下,lambda函数总是返回一个const,而当我们在参数列表后面注明了“mutable”关键字之后,则可以取消其常量性质。若在lambda中使用了mutable修饰符,则“参数列表”是不可省略掉的(即使是参数为空)。

->return-type: 函数的返回值类型。和C/C++中的普通函数返回值类型的性质一样。主要目的是用来追踪lambda函数(有返回值情况下)的返回类型。若lambda函数不需要返回值,则可以直接将这部分省略掉

{statement}:函数体。在该函数体中,除了可以使用参数列表中的变量外,还可以使用所有捕获到的变量(即[capture] 中的变量)。
【示例】

    // 指明返回类型
    auto add = [](int a, int b) -> int { return a + b; };
    // 自动推断返回类型
    auto multiply = [](int a, int b) { return a * b; };

    int sum = add(2, 5);   // 输出:7
    int product = multiply(2, 5);  // 输出:10

Lamda表达式中“捕捉列表”详解

    C++11中的lambda函数,其中的“捕捉列表”是由0个或多个“捕捉项”组成,并以逗号“,”分隔。捕捉列表有如下几种形式:

    (1)[]默认不捕获任何变量;

    (2)[var]表示值传递方式捕捉变量var;

    (3)[=]表示值传递方式捕捉所有副作用域的变量(包括this);

    (4)[&var]表示引用传递捕捉所有变量var;

    (5)[&] 表示引用传递捕捉所有父作用域的比哪里(包括this);

    (6)[=, &x]表示以值捕获所有变量,当x例外,通过引用捕获;

    (7)[&, x]表示以引用捕获所有变量,但x例外,通过值捕获;

    (8)[this] 表示引用捕获当前对象(其实是复制指针);

    (9)[*this]表示通过值方式捕获当前对象;
【示例1】

#include <iostream> 
#include <string> 
#include <stdio.h> 

using namespace std; 

int main() 
{ 
    int a = 1,b =2, c =3; 
    auto retVal = [=,&a,&b]() mutable->int //父作用域内变量a、b以引用方式捕获,其余变量以值捕获方式捕获
    { 
        printf("inner c[%d]\n",c); 
        a = 10; 
        b = 20; 
        c = 30; 
        printf("inner c2[%d]\n",c); 
        return a+b; 
    }; 
    printf("sum[%d]\n",retVal()); 
    printf("a[%d] b[%d] c[%d]\n",a,b,c); 
    return 0; 
}

执行结果:

    inner c[3]
    inner c2[30]
    sum[30]
    a[10] b[20] c[3]

【示例2】

#include <iostream> 
#include <string> 
#include <stdio.h> 

using namespace std; 

int main() 
{ 
    int a = 1,b =2, c =3; 
    auto retVal = [&]() mutable->int //默认引用捕获方式捕获所有父作用域变量
    { 
        printf("inner a[%d] b[%d] c[%d]\n",a,b,c); 
        a = 10; 
        b = 20; 
        c = 30; 
        return a+b; 
    }; 
    printf("sum[%d]\n",retVal()); 
    printf("a[%d] b[%d] c[%d]\n",a,b,c); 
    return 0; 
}

执行结果:

    inner a[1] b[2] c[3]
    sum[30]
    a[10] b[20] c[30]

关于C++11中Lamda表达式更详尽的内容参见:https://www.cnblogs.com/Braveliu/p/4231818.html

二、可变参数模板

    在C++11之前,类模板和函数模板只能含有固定数量的模板参数。C++11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板

    可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号“…”:

template<class ... T> 
void func(T ... args)//T叫模板参数包,args叫函数参数包 
{
	//可变参数模板函数 

} 
func(); // OK:args不含有任何实参 
func(1); // OK:args含有一个实参:int 
func(2, 1.0); // OK:args含有两个实参int和double

1、可变参数模板函数

定义
    一个可变参数模板函数的定义如下:

#include <iostream> 

using namespace std; 

template<class ... T> 
void func(T ... args) 
{
    //可变参数模板函数 
    //sizeof...(sizeof后面有3个小点)计算变参个数 
    cout << "num = " << sizeof...(args) << endl; 
} 
int main() 
{ 
    func(); // num = 0 
    func(1); // num = 1 
    func(2, 1.0); // num = 2 
    return 0; 
}

执行结果:

    num = 0
    num = 1
    num = 2

参数包的展开
(1)递归方式展开
    通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数。

#include <iostream> 

using namespace std; 
//递归终止函数 
void debug() 
{ 
    cout << "empty\n"; 
} 
//展开函数 
template <class T, class ... Args> 
void debug(T first, Args ... last) 
{ 
    cout << "parameter " << first << endl; debug(last...); 
} 
int main() 
{ 
    debug(1, 2, 3, 4); 
    return 0; 
}

执行结果:

    parameter 1
    parameter 2
    parameter 3
    parameter 4
    empty

(2)非递归方式展开

#include <iostream> 
using namespace std; 

template <class T> 
void print(T arg) 
{ 
    cout << arg << endl; 
} 

template <class ... Args> 
void expand(Args ... args) 
{ 
    int a[] = { (print(args), 0)... }; 
} 
int main() 
{ 
    expand(1, 2, 3, 4); 
    return 0; 
}

执行结果:

    1
    2
    3
    4

【示例】通过可变参数模板实现打印函数

#include <iostream> 
#include <stdexcept> 
using namespace std; 

void Debug(const char* s) 
{ 
    while (*s) 
    { 
        if (*s == '%' && *++s != '%') 
        { 
            throw runtime_error("invalid format string: missing arguments"); 
        } 
        cout << *s++; 
    } 
} 

template<typename T, typename... Args> 
void Debug(const char* s, T value, Args... args) 
{ 
    while (*s) 
    { 
        if (*s == '%' && *++s != '%') 
        { 
            cout << value; return Debug(++s, args...); 
        } 
        cout << *s++; 
    } 
    throw runtime_error("extra arguments provided to Debug"); 
} 
int main() 
{ 
    Debug("a = %d, b = %c, c = %s\n", 250, 'm', "mike"); 
    return 0; 
}

执行结果:

    a = 250, b = m, c = mike

2、可变参数模板类
(1)继承方式展开参数包
    可变参数模板类的展开一般需要定义2 ~ 3个类,包含类声明和特化的模板类:

#include <iostream> 
#include <typeinfo> 

using namespace std; 

template<typename... A> class BMW{}; // 变长模板的声明 

template<typename Head, typename... Tail> // 递归的偏特化定义 
class BMW<Head, Tail...> : public BMW<Tail...> 
{//当实例化对象时,则会引起基类的递归构造 
public: 
    BMW() 
    { 
        printf("type: %s\n", typeid(Head).name()); 
    } 
    Head head; 
}; 

template<> class BMW<>{}; // 边界条件 
int main() 
{ 
    BMW<int, char, float> car; 
    return 0; 
}

执行结果:

    type:f
    type:c
    tepy:i

(2)模板递归和特化方式展开参数包

#include <iostream> 
using namespace std; 

template <long... nums> struct Multiply;// 变长模板的声明 

template <long first, long... last> 
struct Multiply<first, last...> // 变长模板类 
{ 
    static const long val = first * Multiply<last...>::val; 
}; 

template<> struct Multiply<> // 边界条件 
{ 
    static const long val = 1; 
}; 
int main() 
{ 
    cout << Multiply<2, 3, 4, 5>::val << endl; // 120 
    return 0; 
}

执行结果:

    120

猜你喜欢

转载自blog.csdn.net/sinat_33924041/article/details/83824952