本篇目标:
学会C++14中新增加的变量模板,泛型lambda,函数返回类型推导
一.变量模板
1.概念
通过之前对C++11的学习,我们已经知道:类模板允许定义一个类的家族,函数模板允许你定义一个函数的家族一样,那么变量模板允许你定义一个变量的家族。
2.使用
在 C++14 之前,如果我们想要定义一个圆周率,并且希望它能根据不同的类型(如 int, float, double)有不同的精度,写法会比较笨重,例如:
cpp
#include<iostream>
using namespace std;
template<class T>
class MathConstants
{
public:
static constexpr T pi = T(3.1415926535897932385L);
};
int main()
{
double pi = MathConstants<double>::pi;
return 0;
}
我们不仅要定义一个类来封装pi,而且使用时很啰嗦。
C++14 引入变量模板后的做法**,** 有了变量模板,可以直接对变量进行模板化,摆脱了毫无意义的类包装,例如:
cpp
template<class T>
constexpr T pi = T(3.1415926535897932385L);
使用时,我们就可以直接这样用:
cpp
cout.precision(6);
cout << "float π: " << pi<float> << endl;
cout.precision(10);
cout << "double π: " << pi<double> << endl;
注:cout.precision(6); 是 中用来控制浮点数(小数)输出精度的语句。
二.泛型lambda
1.C++11与C++14对比使用
假如我们想写一个简单的 Lambda 来实现两个变量相加,如果用 Lambda来完成**,** 那么就需要把类型写死,例如想加 int,得写一个;如果想加 double,又得再写一个,如代码所示:
cpp
int main()
{
auto SumInt = [](int a, int b) { return a + b; };
auto SumDouble = [](double a, double b) { return a + b; };
return 0;
}
但是在C++14允许lambda表达式使用auto作为参数类型,使其成为泛型,如代码所示:
cpp
auto add = [](auto a, auto b) { return a + b; };
cout << add(1, 2) << endl; // 传入 int,推导出 int,输出 3
cout << add(3.14, 2.5) << endl; // 传入 double,推导出 double,输出 5.64
那么lambda可以使用auto后,那么也可以使用auto&代表左值引用的形参,auto&&代表万能引用的的形参,auto&&...代表可变模板参数的万能引用。,如代码所示:
cpp
auto getMax = [](const auto& a, const auto& b) {return a > b ? a : b;};
int main()
{
cout << "最大整数: " << getMax(10, 20) << std::endl;
cout << "最大字符串: " << getMax(string("apple"), string("banana")) << endl;
return 0;
}
cpp
auto func = [](auto&& x, auto& y)
{
x += 97;
y += 97;
};
int main()
{
int i = 10, j = 1;
func(j, i);
cout << "i=" << i << endl;
cout << "j=" << j << endl;
return 0;
}
cpp
int main()
{
vector<string> v;
auto f1 = [&v](auto&&... ts)
{
v.emplace_back(forward<decltype(ts)>(ts)...);
};
string s1 = "love you";
f1(s1);
f1(move(s1));
f1("1111111");
f1(10, 'y');
for (auto& e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
2.补充
1.C++14允许在lambda捕获中使用表达式初始化捕获的变量,这个变量可以是当前域定义的,也可以是没有定义的 ,例如:
cpp
int main() {
vector<int> numbers = { 1, 2, 3, 4, 5 };
auto p = make_unique<int>(10);
auto lambda1 = [value = 5, ptr = std::move(p), &numbers]()
{
cout << "捕获的值: " << value << endl;
cout << "捕获的智能指针值: " << *ptr << endl;
cout << "捕获的vector大小: " << numbers.size() << endl;
};
lambda1();
return 0;
}
注意:捕获的值得是其所在作用域非静态局部变量,否则会报错。
2.C++20以后开始支持在捕捉列表和参数列表中间直接类似模板语法写模板参数,具体参考下面的代码。
cpp
int main()
{
auto add = []<class T>(T a, T b) { return a + b; };
cout << add(10, 20) << endl;
cout << add(52.3, 20.60) << endl;
return 0;
}
当然也可以这样写:
cpp
int main()
{
auto glambda = []<class T>(T a, auto && b) { return a < b; };
cout << add(10, 20) << endl;
cout << add(52.3, 20.60) << endl;
return 0;
}
cpp
int main()
{
vector<string> v;
auto f1 = [&v]<class... Args>(Args&&... ts)
{
v.emplace_back(forward<Args>(ts)...);
};
string s1 = "love you";
f1(s1);
f1(move(s1));
f1("1111111");
f1(10, 'y');
for (auto& e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
三.函数返回类型推导
1.C++11与C++14对比使用
C++11中普通函数用auto做函数返回类型推导时,⼀般要求要配合尾置返回类型使用,相对比较局 限,例如:
cpp
auto add(auto a, auto b) -> decltype(a + b)
{
return a + b;
}
int main()
{
cout << add(1, 2) << endl;
string s1("like");
string s2("you");
cout << add(s1,s2) << endl;
return 0;
}
cpp
int x = 1;
auto f1() -> int { return x; }
auto f2() -> int& { return x; }
auto f3(int x) -> decltype(x * 1.5) { return x * 1.5; } //
int main()
{
cout << f1() << endl;
int& ret = f2();
cout << f3(3) << endl;
ret++;
cout << x << endl;
cout << ret << endl;
return 0;
}
C++14后普通函数可以直接用auto 做返回类型,自动推导返回类型,如果有多条返回语句,那么它们必须推导出相同的类型,例如:
cpp
int x = 1;
auto f1() { return x; }
auto& f2() { return x; }
auto f3(int x) { return x * 1.5; }
auto f4(int x)
{
if (x > 0)
return 1.0;
else
return 2; // 报错,多返回语句需类型一致
}
但是我们也知道:auto无法推导&,顶层const,如果我们想要准确的返回类型,此时就可以使用decltype(auto),例如:
cpp
int x = 1;
decltype(auto) f1() { return x; }
decltype(auto) f2() { return (x); }
template<typename F, typename... Args>
decltype(auto) call(F && f, Args&&... args)
{
return forward<F>(f)(forward<Args>(args)...);
}
int main()
{
cout << f1() << endl;
int& ret = f2();
ret++;
cout << x << endl;
cout << ret << endl;
return 0;
}