22、【C++】C++14常用特性
22、【C++】C++14常用特性
目录
- 一、核心语言特性
- 二、标准库新增特性
- 三、C++14特性应用场景
- 四、C++14迁移与编译器支持
一、核心语言特性
1.1 泛型lambda表达式(Generic Lambda)
1.1.1 auto参数实现泛型
C++14允许lambda的参数使用auto,实现泛型匿名函数:
// 泛型lambda,可接受任意类型参数
auto add = [](auto x, auto y) {
return x + y;
};
int main() {
int sum1 = add(1, 2); // int版本,返回3
double sum2 = add(1.5, 2.5); // double版本,返回4.0
std::string sum3 = add(std::string("a"), std::string("b")); // string版本,返回"ab"
return 0;
}
编译器会为不同参数类型实例化多个lambda版本,类似函数模板。
1.1.2 泛型lambda与函数对象的对比
泛型lambda本质是带有模板operator()的函数对象,但语法更简洁:
// C++11:需手动定义函数对象
struct Add {
template <typename T, typename U>
auto operator()(T x, U y) const -> decltype(x + y) {
return x + y;
}
};
Add add;
// C++14:泛型lambda等价于上述函数对象
auto add = [](auto x, auto y) { return x + y; };
1.1.3 在标准算法中的应用
泛型lambda简化算法的通用化调用:
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v1 = {1, 2, 3};
std::vector<double> v2 = {1.1, 2.2, 3.3};
// 泛型lambda遍历不同类型容器
auto print = [](const auto& container) {
for (const auto& elem : container) {
std::cout << elem << " ";
}
std::cout << std::endl;
};
print(v1); // 打印int容器
print(v2); // 打印double容器
return 0;
}
1.2 函数返回类型推导(Return Type Deduction)
1.2.1 非模板函数的返回类型推导
C++14允许非模板函数省略返回类型,编译器根据return语句推导:
// 自动推导返回类型为int
auto add(int a, int b) {
return a + b;
}
// 推导为double
auto multiply(double a, double b) {
return a * b;
}
限制:函数体内必须有return语句,且所有return语句返回同一类型。
1.2.2 与尾置返回类型的配合
对于复杂返回类型(如模板函数),可结合尾置返回类型:
template <typename T, typename U>
auto add(T a, U b) -> decltype(a + b) { // C++11/14
return a + b;
}
template <typename T, typename U>
auto add(T a, U b) { // C++14可省略尾置返回类型
return a + b;
}
1.2.3 推导规则与限制
- 推导规则:返回类型为return表达式的类型。
- 限制:
- 无法推导void返回类型(需显式指定)。
- 递归函数需至少一个return语句在推导前出现。
1.3 变量模板(Variable Templates)
1.3.1 基本语法与实例化
变量模板允许定义带模板参数的变量:
template <typename T>
constexpr T pi = T(3.14159265358979323846);
int main() {
double pi_d = pi<double>; // 3.14159...
float pi_f = pi<float>; // 3.14159f
return 0;
}
1.3.2 跨类型常量定义
变量模板可定义适用于多种类型的常量,如不同精度的数学常数:
template <typename T>
constexpr T e = T(2.718281828459045);
template <typename T>
constexpr T golden_ratio = T(1.61803398875);
1.4 constexpr函数扩展
1.4.1 C++14 vs C++11的 constexpr 函数限制
| 特性 | C++11 constexpr函数 | C++14 constexpr函数 |
|---|---|---|
| 允许语句 | 仅允许return语句 | 允许if、switch、for、while等语句 |
| 局部变量 | 不允许 | 允许,但必须初始化且为字面量类型 |
| 递归 | 允许 | 允许 |
1.4.2 允许循环与条件语句
C++14 constexpr函数可包含循环和条件判断:
// C++14 constexpr函数:计算n的阶乘
constexpr int factorial(int n) {
if (n <= 1) return 1;
int result = 1;
for (int i = 2; i <= n; ++i) {
result *= i;
}
return result;
}
constexpr int fact_5 = factorial(5); // 编译期计算,值为120
1.4.3 编译期计算案例
constexpr函数可用于编译期数组初始化:
constexpr int compute_size(int n) {
return n * 2;
}
int main() {
constexpr int size = compute_size(5); // 编译期计算size=10
int arr[size]; // 合法,数组大小为编译期常量
return 0;
}
1.5 [[deprecated]]属性
1.5.1 标记过时实体
[[deprecated]]属性用于标记过时的函数、类或变量,编译器会发出警告:
[[deprecated]]
void old_function() {
// 旧实现
}
int main() {
old_function(); // 编译器警告:'old_function' is deprecated
return 0;
}
1.5.2 带消息的deprecated
可添加消息说明过时原因和替代方案:
[[deprecated("use new_function instead")]]
void old_function() {}
void new_function() {}
int main() {
old_function(); // 警告:'old_function' is deprecated: use new_function instead
return 0;
}
1.6 二进制字面量与数字分隔符
1.6.1 二进制字面量(0b前缀)
C++14引入0b前缀表示二进制数:
constexpr int flag1 = 0b0001; // 1
constexpr int flag2 = 0b0010; // 2
constexpr int combined = flag1 | flag2; // 0b0011 = 3
1.6.2 数字分隔符(')
'可作为数字分隔符,增强大数字的可读性:
constexpr long long population = 7'800'000'000; // 78亿
constexpr double pi = 3'141'592'653'589'793'238'46; // 可读性提升
注意:分隔符不能位于数字开头或结尾,也不能连续使用。
1.7 decltype(auto)
1.7.1 推导规则
decltype(auto)结合decltype和auto的推导规则:
- 若表达式为
x,则推导为decltype(x)。 - 常用于转发函数,保留表达式的值类别(左值/右值)。
1.7.2 在转发函数中的应用
// 转发函数:返回值类型与f(args)一致
template <typename F, typename... Args>
decltype(auto) forward_result(F&& f, Args&&... args) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
// 测试:若f返回左值引用,forward_result也返回左值引用
int x = 10;
auto& ref = forward_result([&]() -> int& { return x; });
二、标准库新增特性
2.1 std::make_unique
2.1.1 安全创建unique_ptr
C++14新增std::make_unique,用于安全创建std::unique_ptr:
#include <memory>
int main() {
// C++14:使用make_unique(推荐)
auto up = std::make_unique<int>(42); // 分配int并初始化为42
// C++11:直接使用new(不推荐,异常安全问题)
std::unique_ptr<int> up_old(new int(42));
return 0;
}
2.1.2 与new的对比
std::make_unique的优势:
- 异常安全:若构造函数抛出异常,避免内存泄漏。
- 简洁:无需重复类型名。
- 避免裸指针:减少直接使用new的机会。
2.2 std::integer_sequence
2.2.1 生成整数序列
std::integer_sequence表示整数序列,配合模板参数包使用:
#include <utility> // for integer_sequence
template <typename T, T... Ints>
void print_sequence(std::integer_sequence<T, Ints...>) {
((std::cout << Ints << " "), ...); // C++17折叠表达式
}
int main() {
print_sequence(std::integer_sequence<int, 1, 2, 3, 4>()); // 输出:1 2 3 4
return 0;
}
2.2.2 在模板元编程中的应用
生成0到N-1的整数序列,用于展开数组或元组:
// 展开数组:对数组每个元素调用f
template <typename T, size_t N, size_t... Is>
void for_each_array(T (&arr)[N], std::index_sequence<Is...>, auto f) {
(f(arr[Is]), ...);
}
int main() {
int arr[] = {1, 2, 3};
for_each_array(arr, std::make_index_sequence<3>(), [](int x) {
std::cout << x << " "; // 输出:1 2 3
});
return 0;
}
2.3 std::exchange
2.3.1 交换值并返回旧值
std::exchange(obj, new_val)将obj赋值为new_val,并返回obj的旧值:
#include <utility>
int main() {
int x = 10;
int old_x = std::exchange(x, 20); // x变为20,old_x为10
return 0;
}
2.3.2 在移动语义中的应用
简化移动构造函数的实现:
class MyString {
public:
MyString(MyString&& other) noexcept {
_data = std::exchange(other._data, nullptr); // 转移资源并置空other._data
_size = std::exchange(other._size, 0);
}
private:
char* _data;
size_t _size;
};
2.4 tuple扩展(按类型访问元素)
C++14允许通过类型而非索引访问tuple元素:
#include <tuple>
#include <string>
int main() {
std::tuple<int, std::string, double> t(42, "hello", 3.14);
// C++11:按索引访问(不直观)
int i = std::get<0>(t);
// C++14:按类型访问(需类型唯一)
std::string s = std::get<std::string>(t);
double d = std::get<double>(t);
return 0;
}
2.5 其他标准库改进
2.5.1 std::quoted
std::quoted在IO操作中为字符串添加引号,避免空格分隔问题:
#include <iomanip>
#include <string>
int main() {
std::string s = "hello world";
std::cout << std::quoted(s) << std::endl; // 输出:"hello world"
return 0;
}
2.5.2 constexpr算法
C++14为部分算法添加constexpr版本,支持编译期计算:
#include <algorithm>
constexpr bool is_sorted() {
constexpr int arr[] = {1, 2, 3, 4};
return std::is_sorted(std::begin(arr), std::end(arr));
}
constexpr bool sorted = is_sorted(); // 编译期计算为true
三、C++14特性应用场景
3.1 泛型lambda简化算法调用
泛型lambda使算法可直接操作不同类型容器:
#include <vector>
#include <list>
#include <algorithm>
int main() {
auto sum = [](const auto& container) {
return std::accumulate(container.begin(), container.end(), 0);
};
std::vector<int> v = {1, 2, 3};
std::list<int> l = {4, 5, 6};
std::cout << sum(v) + sum(l) << std::endl; // 21
return 0;
}
3.2 constexpr函数实现编译期数组初始化
constexpr int generate_array(int n) {
int arr[100];
for (int i = 0; i < n; ++i) {
arr[i] = i * i; // 编译期计算平方
}
return arr[5]; // 返回25
}
constexpr int val = generate_array(10); // 编译期计算val=25
3.3 变量模板定义跨类型常量
template <typename T>
constexpr T gravity = T(9.80665); // 重力加速度
// 不同单位下的重力加速度
constexpr double g_mps2 = gravity<double>; // m/s²
constexpr float g_fts2 = gravity<float> * 3.28084f; // ft/s²
四、C++14迁移与编译器支持
4.1 代码迁移策略
- 渐进式迁移:优先使用C++14简化代码,如泛型lambda和返回类型推导。
- 替换C++11临时方案:用
std::make_unique替代自定义工厂函数。 - 利用 constexpr 扩展:将编译期计算逻辑迁移到constexpr函数。
4.2 主流编译器支持情况
- GCC:4.9及以上支持大部分C++14特性。
- Clang:3.4及以上支持。
- MSVC:Visual Studio 2015及以上支持。
以上内容,全面覆盖了C++14的核心语言特性和标准库改进。C++14作为C++11的增量更新,显著提升了代码简洁性和表达能力,尤其在泛型编程和编译期计算方面。合理应用这些特性可使代码更简洁、高效。
更多推荐
所有评论(0)