C++14新特性
函数返回值类型推导
#include <iostream>
using namespace std;
// C++14写法
template<typename T, typename U>
auto func1(T t, U u) {
return t + u;
}
// C++11写法
template<typename T, typename U>
auto func2(T t, U u)->decltype(t + u) {
return t + u;
}
int main() {
cout << func1(4, 5) << endl;
cout << func2(5.5, 6.6) << endl;
return 0;
}
C++11时,推断函数返回值类型需要将返回值类型后置并借助decltype关键词,比较麻烦。对此,C++14拓展了auto的范围,可以直接推到函数返回值类型。但是,应用auto推导返回值类型也有一些限制。
一:函数内如果有多个return语句,它们必须返回相同的类型,否则编译失败
二:如果return语句返回初始化列表,返回值类型推导也会失败
tips:初始化列表没有类型,因此auto无法进行类型推导
三: 如果函数是虚函数,不能使用返回值类型推导
四:返回类型推导可以用在递归函数中,但是递归调用必须以至少一个返回语句作为先导,以便编译器推导出返回类型
auto sum(int i) {
if (i == 1)
return i; // return int
else
return sum(i - 1) + i; // ok
}
泛型lambda
#include <iostream>
using namespace std;
// C++14写法
auto func1 = [](auto a) { return a; };
// C++11写法
auto func2 = [](int a) { return a; };
int main() {
cout << func1(1.1f) << endl;
cout << func2(2) << endl;
}
在C++11中,lambda表达式参数需要使用具体的类型声明,而C++14可以使用auto。这样,lambda表达式也可以用于泛型编程。
lambda初始化表使用表达式和move
C++11 的 lambda 函数通过值拷贝(by copy)或引用(by reference)捕获(capture)已在外层作用域声明的变量。这意味着 lambda 的值成员不可以是 move-only 的类型。C++14允许被捕获的成员用任意的表达式初始化。这既允许了 capture by value-move,也允许了任意声明 lambda 的成员,而不需要外层作用域有一个具有相应名字的变量。
#include <iostream>
using namespace std;
int main() {
auto func = [val = 3]{return val;};
auto ptr = std::make_unique<int>(10); //See below for std::make_unique
auto lambda = [ptr = std::move(ptr)]{ return *ptr; };
cout << func() << endl;
cout << lambda() << endl;
}
运行结果:
3
10
decltype(auto)
在C++11中,我们使用auto和decltype都可以推断类型。二者的主要区别在于auto推断会丢失引用类型信息,而decltype推断保留引用类型信息。auto和decltype的用法不同,auto直接用于定义变量,decltype只生成类型。decltype用途广,可以推导任意表达式的类型,如果该表达式长度过长,就非常不方便了。于是C++14推出了decltype(auto),用法如下:
int x = 1, y = 2, c = 3, d = 4;
float e = 3.0;
// C++14写法
decltype(auto)z1 = x + y + c + d + e;
// C++11写法
decltype(x + y + c + d + e)z2 = x + y + c + d + e;
decltype(auto)中的auto其实就是一个占位符,内容就是右侧的表达式。
拓展constexpr关键字
放宽了对constexpr函数的限制:在 C++11 中,constexpr 函数的函数体只能有using 指令、typedef 语句、static_assert 断言、空语句,只能有一条return 语句,并且该语句必须是常量表达式。而在 C++14 中,constexpr 函数可以包含多条语句,可以有条件语句(如 if)和循环语句(如 for、while),只要在编译时能够被求值为常量表达式即可。例如:
constexpr int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; ++i) {
result *= i;
}
return result;
}
constexpr int result = factorial(5); // 在编译时计算出结果 120
变量模板
template<class T>
constexpr T pi = T(3.1415926535897932385L); // variable template
int main()
{
std::cout << pi<double> << std::endl;
std::cout << pi<float> << std::endl;
std::cout << pi<int> << std::endl;
return 0;
}
// 输出结果
3.14159
3.14159
3
二进制字面值
二进制字面值可以使用0b或者0B开头来表示
int i = 0b0100010001; // 273
int i = 0B0100010001; // 273,大写B
数值分位符
数位分隔符不影响数值,只是有了跟好的可读性。
long k = 1'234'567'890
通过类型寻址元组
C++11 引入的 std::tuple 类型允许不同类型的值的聚合体用编译期整型常数索引。C++14还允许使用类型代替常数索引,从多元组中获取对象。若多元组含有多于一个这个类型的对象,将会产生一个编译错误:
tuple<string, string, int> t("foo", "bar", 7);
int i = get<int>(t); // i == 7
int j = get<2>(t); // Same as before: j == 7
string s = get<string>(t); //Compiler error due to ambiguity
make unique
std::make_unique 可以像 std::make_shared 一样使用,用于产生 std::unique_ptr 对象
std::unique_ptr<int> ptr = std::make_unique<int>(42);
std::exchange
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v1 { 1,2,3,4 };
vector<int> v2 { 2,2,2,2 };
v2 = exchange(v1, v2);
}
可以看下exchange的实现:
template<class T, class U = T>
constexpr T exchange(T& obj, U&& new_value) {
T old_value = std::move(obj);
obj = std::forward<U>(new_value);
return old_value;
}
std::exchange和std::swap作用相同,只是std::exchange实现了完美转发。
标准自定义字面量
C++11 增加了自定义字面量(user-defined literals)的特性,使用户能够定义新的字面量后缀,但标准库并没有对这一特性加以利用。C++14 标准库定义了以下字面量后缀:
“s”,用于创建各种 std::basic_string 类型。
“h”、“min”、“s”、“ms”、“us”、“ns”,用于创建相应的 std::chrono::duration 时间间隔。
using namespace std::literals;
std::string str = "hello world"s;
std::chrono::seconds dur = 60s;