C++14在C++11的基础上进一步扩展和优化,带来了不少新特性,以下是一些主要的方面:
1. 变量模板
- 概念与作用:变量模板允许定义模板化的变量,就像函数模板可以根据不同类型生成不同的函数一样,变量模板可以依据不同类型生成不同的变量实例。这使得代码能够以更通用、更灵活的方式处理各种类型相关的数据,尤其在编写泛型代码时非常有用。
- 示例代码:
template<typename T>
constexpr T pi = T(3.1415926535897932385);
int main() {
std::cout << "int类型的pi值: " << pi<int> << std::endl;
std::cout << "double类型的pi值: " << pi<double> << std::endl;
return 0;
}
在上述代码中,定义了一个变量模板 pi
,根据传入的不同类型 T
,可以生成对应类型的 pi
值。在 main
函数中,分别实例化了 int
类型和 double
类型的 pi
,展示了变量模板能方便地针对不同类型提供相应常量值的特性。
2. 泛型 lambda 表达式
- 概念与作用:C++11 中引入了 lambda 表达式,方便在代码中就地定义匿名函数,但 C++11 的 lambda 表达式在处理泛型方面比较受限。C++14 增强了这一特性,允许 lambda 表达式使用
auto
类型说明符来进行参数类型推导,使其具有了泛型的能力,这样的 lambda 可以像模板函数一样适用于多种不同类型的参数,增强了代码的简洁性和通用性。 - 示例代码:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 3, 2, 4};
std::vector<double> dvec = {1.1, 3.3, 2.2, 4.4};
// 使用泛型lambda表达式对整数向量进行排序
std::sort(vec.begin(), vec.end(), [](auto a, auto b) { return a < b; });
// 使用相同的泛型lambda表达式对双精度向量进行排序
std::sort(dvec.begin(), dvec.end(), [](auto a, auto b) { return a < b; });
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
for (double num : dvec) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
这里定义的 lambda 表达式 [](auto a, auto b) { return a < b; }
能够自动根据传入的参数类型(无论是 int
还是 double
等)进行类型推导并执行相应的比较操作,无需为不同类型的容器编写多个特定类型的排序 lambda 表达式,提高了代码复用性。
3. 返回类型推导的扩展
- 概念与作用:C++11 就开始支持函数返回类型推导,但有一定限制,例如函数体中必须是单一的
return
语句等情况才能方便地使用返回类型推导。C++14 放宽了这些限制,使得更多函数可以利用返回类型推导功能,即使函数体中有多个语句、复杂的控制流等情况,只要最终能明确推导出返回类型,都可以使用该特性,进一步简化了代码编写,尤其是对于一些复杂的泛型函数等场景。 - 示例代码:
#include <iostream>
// 一个简单的函数,根据条件返回不同类型的值,利用C++14扩展的返回类型推导
auto get_value(bool condition) {
if (condition) {
return 5; // 可以推导出返回类型为int
} else {
return 3.14; // 也可以推导出返回类型为double
}
}
int main() {
std::cout << get_value(true) << std::endl;
std::cout << get_value(false) << std::endl;
return 0;
}
在 get_value
函数中,根据传入的布尔值条件返回不同类型的值,C++14 能够正确推导出返回类型,使得编写这样的函数更加简洁,无需显式指定返回类型(当然,前提是编译器能根据代码逻辑明确推导出类型)。
4. 二进制字面量
- 概念与作用:在以往的C++代码中,如果要表示二进制数,通常需要先将其转换为十六进制或八进制等形式,再进行赋值等操作,不够直观。C++14 引入了二进制字面量的表示方法,允许直接使用
0b
或0B
前缀来表示二进制数,这使得在处理与二进制相关的数据(比如位操作、底层硬件交互等场景)时更加清晰、便捷。 - 示例代码:
int main() {
int num = 0b1010; // 直接用二进制字面量表示十进制的10
std::cout << "二进制1010对应的十进制数: " << num << std::endl;
return 0;
}
通过 0b1010
这种形式直观地表示了一个二进制数,编译器会将其转换为对应的十进制数值(这里是10),方便了对二进制数据的书写和操作。
5. 数字分隔符
- 概念与作用:对于较长的数字字面量(如大整数、浮点数等),为了提高其可读性,C++14 允许使用单引号(
'
)作为数字分隔符,将数字按一定逻辑进行分隔,使其更易于理解和查看,同时又不影响数字本身的数值含义。 - 示例代码:
int main() {
long long big_num = 123'456'789'012'345; // 使用数字分隔符表示一个大整数
double big_float = 1.234'567'89; // 对浮点数也可以使用数字分隔符
std::cout << "大整数: " << big_num << std::endl;
std::cout << "大浮点数: " << big_float << std::endl;
return 0;
}
在上述代码中,通过数字分隔符把长数字进行了合理划分,使其在代码中更直观地展示数值大小,方便阅读和代码维护,尤其是在处理金融、科学计算等涉及到大数值的领域很有帮助。
6. 函数对象的改进
constexpr
函数对象:C++14 允许用户自定义的函数对象(例如重载了operator()
的类)可以标记为constexpr
,意味着这样的函数对象可以在编译期执行,用于在编译阶段计算一些常量表达式,这对于优化性能、提高代码的灵活性以及在模板元编程等场景下有很大的作用。- 示例代码:
#include <iostream>
class Add {
public:
constexpr int operator()(int a, int b) const {
return a + b;
}
};
int main() {
constexpr Add add_obj;
constexpr int result = add_obj(3, 5); // 在编译期就能计算出结果
std::cout << "编译期计算结果: " << result << std::endl;
return 0;
}
这里定义的 Add
类作为函数对象,其 operator()
被标记为 constexpr
,所以在 main
函数中可以在编译期就计算出 3 + 5
的结果,并且将其作为常量表达式使用,这在一些对性能和编译期计算有要求的场景中非常有用。
7. 聚合初始化的扩展
- 概念与作用:在C++中,聚合初始化是一种方便的初始化对象的方式,C++14 对其进行了扩展,使得在初始化聚合体(如结构体、数组等)时更加灵活,减少了一些之前需要遵循的严格限制,比如可以使用花括号初始化包含继承关系的聚合类对象等,让代码编写更加简洁自然。
- 示例代码:
#include <iostream>
struct Base {
int base_num;
};
struct Derived : public Base {
int derived_num;
};
int main() {
// 使用扩展的聚合初始化方式初始化派生类对象
Derived d = {1, 2};
std::cout << "Base成员值: " << d.base_num << ", Derived成员值: " << d.derived_num << std::endl;
return 0;
}
在这个例子中,通过 {1, 2}
这种简单的花括号初始化方式就可以对包含继承关系的 Derived
类对象进行初始化,编译器会按照相应顺序(先初始化基类成员,再初始化派生类成员)正确地设置对象的成员值,体现了聚合初始化扩展后的便利性。
8. 放宽的 constexpr
函数限制
- 概念与作用:C++11 中引入了
constexpr
函数,但对其有诸多限制,比如函数体不能包含复杂的语句(如循环、多个返回语句等)。C++14 放宽了这些限制,允许constexpr
函数中出现更多的语句类型,例如可以有局部变量、简单的循环等,这使得constexpr
函数能够处理更复杂的计算逻辑,同时又能在编译期求值,进一步拓展了编译期计算的能力,有助于优化代码性能和提升代码的通用性。 - 示例代码:
#include <iostream>
constexpr int factorial(int n) {
int result = 1;
for (int i = 2; i <= n; ++i) {
result *= i;
}
return result;
}
int main() {
constexpr int fact_5 = factorial(5); // 在编译期计算5的阶乘
std::cout << "5的阶乘: " << fact_5 << std::endl;
return 0;
}
在 factorial
函数中,通过使用 constexpr
标记并在函数体中包含了循环语句来计算阶乘,C++14 允许这样的操作,并且可以在编译期完成计算,将结果作为常量表达式使用,展示了放宽 constexpr
函数限制后的强大功能。
这些只是C++14众多新特性中的一部分,它们从不同方面提升了C++语言的表达能力、编程便利性以及性能优化等,使得开发者可以更高效地编写高质量的C++代码。