C++14新特性_第一章语言特性_函数返回类型推导,泛型Lambda
本文介绍C++14新特性,函数返回类型推导和泛型Lambda表达式。
文章目录
第一章 C++14语言特性
1.1 函数返回类型推导
本文介绍C++14新特性,函数返回类型推导 (Function Return Type Deduction)
C++的演进历程中,每一次标准更新都让这门“古老”的语言变得更加现代和易用。C++11引入了auto关键字,让我们尝到了“语法糖”的甜头,给编程带来了极大便利;而C++14将auto的功能扩展到了函数的返回值上。
1.1.1 函数返回值后置提出背景
C++11中,虽然可以使用auto推导变量类型,但是auto不能推导函数的返回值。某些情况下,让编译器推导函数的返回值,必须使用尾置返回类型(Trailing Return Type)方式实现。
在处理函数模板时,这种尾置返回值方式比较常用,使用方式如下:
template<typename T,typename U>
auto add(T a, U b)->decltype(a + b)
{
return a + b;
}
void test()
{
int ret = add(1, 2.2);
}
这种方式必须在函数后边写出: ->decltype(…)代码,为了看起来更直观,C++14引入了函数返回类型推导,告别了这样多余的写法。
1.1.2 函数返回类型推导功能说明
C++14中,可以直接将函数的返回类型声明为auto,不必再写尾置返回值,编译器会分析函数内return的返回值类型,并自动推导出返回值类型。
需要注意的是:auto 默认按值返回,如果需要返回引用 或者 const属性,需要在auto位置添加这些属性。
举例如下:
示例1:编译器自动推导a+b类型
// C++11 尾置返回值
template<typename T,typename U>
auto add(T a, U b)->decltype(a + b)
{
return a + b;
}
// C++14 简化尾置返回值
template<typename T,typename U>
auto add2(T a, U b)
{
return a + b;
}
void test()
{
int ret = add(1, 2.2);
int ret2 = add2(1, 2.2);
}
示例2:返回引用,使用auto + & 即可返回引用类型。
int x = 10;
// 返回引用方式:auto &
auto& getX()
{
return x;
}
void test()
{
getX() = 3;
}
1.2 泛型Lambda
C++11中引入Lambda表达式,改变了我们编写回调函数和算法的方式,为我们带来了极大的便利。然而,C++11中的Lambda表达式有一个缺陷是,参数类型必须显示指定,比如 实现一个能同时处理int和double的lambda,往往需要写两个Lambda表达式,一个接收int,一个接收double类型,虽然处理逻辑相同,但是确引入了两个Lambda表达式。C++14为了解决这个问题,引入了泛型Lambda(Generic Lambda)。
1.2.1 Lambda表达式问题说明
在C++11中,Lambda参数类型是定义时就确定好的,不支持泛型方式。
比如,要实现处理两个double int 类型的lambda,C++11实现方式如下:
void test()
{
auto sumInt = [](int a, int b) { return a + b; };
auto sumDouble = [](double a, double b) { return a + b; };
cout << "sumInt = " << sumInt(2, 3) << endl;
// sumInt = 5
cout << "sumDouble = " << sumDouble(2.5, 3.5) << endl;
// sumDouble = 6
}
这种方式显示增加了代码的冗余性,为了解决这个问题,可以使用函数模板,但是因为处理逻辑比较简单,直接定义函数模板,有些“杀鸡用牛刀”的感觉。
还有一种场景,在使用标准库中的算法时,如果第三个参数需要一个函数对象,此时lambda表达式的参数仍然要显示指定,如下:
void test()
{
std::map<std::string, std::vector<int>> myMap;
myMap["numbers"] = { 1, 2, 3, 4, 5 };
myMap["name"] = { 10, 20, 30 };
// C++11: 必须显示指定lambda参数类型
std::for_each(myMap.begin(), myMap.end(),
[](const std::pair<std::string, std::vector<int>>& pair)
{
// ...
cout << " key = " << pair.first << endl;
});
/* 输出结果:
key = name
key = numbers
*/
}
上面例子中,Lambda表达式都需要显示指定明确的类型,不支持自动推导。在第二个例子中,如果参数比较长,写起来不是很方便。为此,C++14支持了泛型Lambda解决这个问题。
1.2.2 泛型Lambda实现原理
泛型Lambda允许我们在Lambda中使用auto关键字。实现原理是,编译器根据实参自动推导出参数类型。
语法格式:
auto lambda = [](auto x, auto y) {
return x + y;
};
泛型Lambda实现原理:编译器看到泛型Lambda表达式后,将其转换成一个类模板,在这个类模板中重载了带参数的operator(),类似于:
struct CompilerGeneratedName {
template <typename T, typename U>
auto operator()(T x, U y) const {
return x + y;
}
};
下面是使用C++14实现上边的两个例子,看如何简化代码。
示例1:基于泛型lambda的改进
void test()
{
auto sumInt = [](int a, int b) { return a + b; };
auto sumDouble = [](double a, double b) { return a + b; };
cout << "sumInt = " << sumInt(2, 3) << endl;
// sumInt = 5
cout << "sumDouble = " << sumDouble(2.5, 3.5) << endl;
// sumDouble = 6
// C++14 泛型lambda
auto sumGeneric = [](auto a, auto b) { return a + b; };
cout << "sumGeneric int = " << sumGeneric(2, 3) << endl;
// sumGeneric int = 5
cout << "sumGeneric double = " << sumDouble(2.5, 3.5) << endl;
// sumGeneric double = 6
}
示例2:泛型lambda在STL算法中的使用
void test()
{
std::map<std::string, std::vector<int>> myMap;
myMap["numbers"] = { 1, 2, 3, 4, 5 };
myMap["name"] = { 10, 20, 30 };
// C++11: 必须显示指定lambda参数类型
std::for_each(myMap.begin(), myMap.end(),
[](const std::pair<std::string, std::vector<int>>& pair)
{
// ...
cout << " key = " << pair.first << endl;
});
/* 输出结果:
key = name
key = numbers
*/
// C++14: 使用auto自动推导参数类型
std::for_each(myMap.begin(), myMap.end(),
[](const auto& pair)
{
cout << " key = " << pair.first << endl;
});
/* 输出结果:
key = name
key = numbers
*/
}
示例3:泛型Lambda + 完美转发
下面例子中,使用Lambda泛型方式,封装一个可变参模板,然后使用使用decltype(auto)确定参数类型,进行完美转发。
void print(int a)
{
cout << "int: " << a << endl;
}
void print(int a, int b)
{
cout << "int int: " << a << ", " << b << endl;
}
void test()
{
// 实现 完美转发的打印函数
auto printer = [&](auto && ...args)
{
print(std::forward<decltype(args)>(args)...);
};
printer(22);
// int: 22
printer(22,33);
// int int: 22, 33
}
1.2.3 总结
泛型Lambda是对C+11Lambda的扩充,使用起来更方便。
更多推荐
所有评论(0)