C++14 核心特性详解
C++14 是 C++11 主要版本之后的次要版本,主要包含语言和标准库的改进与缺陷修复,增强了代码简洁性和表达能力。
目录
C++14
- [C++14 核心特性详解](#C++14 核心特性详解)
-
- 目录
- [1. 变量模板](#1. 变量模板)
- [2. 泛型lambda表达式](#2. 泛型lambda表达式)
- [3. 函数返回类型推导](#3. 函数返回类型推导)
- [4. 二进制字面量](#4. 二进制字面量)
- [5. 数字分隔符](#5. 数字分隔符)
- [6. 使用非静态成员初始化器的聚合类](#6. 使用非静态成员初始化器的聚合类)
- [7. std::exchange](#7. std::exchange)
- [8. std::make_unique](#8. std::make_unique)
- [9. std::integer_sequence](#9. std::integer_sequence)
- [10. std::quoted](#10. std::quoted)
- [11. shared_timed_mutex 和 shared_mutex](#11. shared_timed_mutex 和 shared_mutex)
- [12. 字面量后缀](#12. 字面量后缀)
1. 变量模板
C++14 允许模板不仅用于类和函数,还可以用于变量,使得编译期常量可以模板化。
cpp
#include <iostream>
using namespace std;
// 定义变量模板
template<typename T>
constexpr T pi = T(3.14159265358979323L);
// 使用变量模板
int main() {
std::cout.precision(10);
std::cout << "float π: " << pi<float> << std::endl;
std::cout.precision(10);
std::cout << "double π: " << pi<double> << std::endl;
return 0;
}
2. 泛型lambda表达式
C++14 允许lambda表达式使用auto作为参数类型,使其成为泛型。包括:
auto&左值引用的形参auto&&万能引用的形参auto&&...可变模板的万能引用
cpp
// 泛型lambda
auto getMax = const auto& x, const auto& y {
return x > y ? x : y;
};
// 初始化捕获
int i = 1;
auto f1 = { // 创建新变量i,用表达式i+1初始化
std::cout << "这个i只在lambda函数体内部可见和使用: " << i << std::endl;
};
3. 函数返回类型推导
C++14 直接支持auto推导函数返回类型,无需尾置返回类型。
cpp
// 返回类型推导
auto add(int x, int y) {
return x + y; // 推导为int
}
// 多返回语句必须推导出相同类型
auto f1(int x) {
if (x > 0)
return 1; // int
else
return 2; // int
}
// decltype(auto) 用于完美转发返回类型
template<typename F, typename... Args>
decltype(auto) call(F&& f, Args&&... args) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
4. 二进制字面量
C++14 增加了二进制字面量表示,提高代码可读性。
cpp
int d = 42; // 十进制
int o = 052; // 八进制
int x = 0x2a; // 十六进制
int X = 0X2A; // 十六进制
int b = 0b101010; // 二进制,C++14新增
5. 数字分隔符
C++14 允许在数字中使用单引号作为分隔符,提高大数字的可读性。
cpp
int million = 100'0000;
long hexValue = 0xDEAD'BEEF;
double pi = 3.141'592'653'59;
unsigned long long bigBinary = 0b1010'1010'1010'1010;
6. 使用非静态成员初始化器的聚合类
C++14 允许聚合类包含默认的非静态成员初始化器。
cpp
struct Employee {
std::string name = "Unknown";
int id = -1;
double salary = 0.0;
};
// 使用
Employee e1 = {"xxx", 1, 1.1}; // C++98风格
Employee e2{"John"}; // name="John", id=-1, salary=0.0
Employee e3{"Alice", 123}; // name="Alice", id=123, salary=0.0
Employee e4{}; // 全部采用初始化器
7. std::exchange
std::exchange 是 C++14 在 <utility> 头文件中引入的实用函数模板,提供简洁高效的方式来替换对象的值并返回其旧值。
cpp
#include <utility>
// 以 new_value 替换 obj 的值,并返回 obj 的旧值
template<class T, class U = T>
T exchange(T& obj, U&& new_value) {
T old = std::move(obj);
obj = std::forward<U>(new_value);
return old;
}
// 使用示例
int x = 10;
int old = std::exchange(x, 20); // x变为20,old为10
8. std::make_unique
std::make_unique 是C++14引入的智能指针创建工具,用于安全地创建和管理std::unique_ptr对象。
cpp
#include <memory>
// 创建单个对象
auto ptr1 = std::make_unique<int>(); // 值初始化为0
auto ptr2 = std::make_unique<int>(42); // 初始化为42
// 创建数组
auto arr = std::make_unique<int[]>(5); // 5个int,值初始化为0
// 创建结构体
struct Vec3 { int x, y, z; };
auto v = std::make_unique<Vec3>(1, 2, 3);
9. std::integer_sequence
std::integer_sequence 是C++14引入的模板类,用于在编译时表示一个整数序列,是模板元编程和编译时计算的有用工具。
cpp
#include <utility>
#include <iostream>
template<typename Array, std::size_t... I>
void print_array_impl(const Array& arr, std::index_sequence<I...>) {
((std::cout << arr[I] << ' '), ...);
}
template<typename T, std::size_t N>
void print_array(const std::array<T, N>& arr) {
print_array_impl(arr, std::make_index_sequence<N>{});
}
int main() {
std::array<int, 4> arr{1, 2, 3, 4};
print_array(arr); // 输出: 1 2 3 4
return 0;
}
10. std::quoted
std::quoted 是C++14引入的I/O操纵器,用于简化带引号字符串的输入输出操作。
cpp
#include <iostream>
#include <sstream>
#include <iomanip>
#include <string>
int main() {
// 输出带引号的字符串
std::string text = "Hello, World!";
std::cout << "Without quoted: " << text << std::endl;
std::cout << "With quoted: " << std::quoted(text) << std::endl;
// 输出:
// Without quoted: Hello, World!
// With quoted: "Hello, World!"
// 输入带引号的字符串
std::istringstream input("\"Hello, World!\"");
input >> std::quoted(text);
std::cout << "Extracted: " << text << std::endl;
// 输出: Extracted: Hello, World!
return 0;
}
11. shared_timed_mutex 和 shared_mutex
C++14 提供了shared_timed_mutex,C++17 提供了shared_mutex,解决读写者问题。
cpp
#include <iostream>
#include <shared_mutex>
#include <thread>
#include <vector>
class ThreadSafeCounter {
public:
ThreadSafeCounter() = default;
// 多个线程可以同时读取
unsigned int get() const {
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
return value_;
}
// 只有一个线程可以修改
void increment() {
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
++value_;
}
private:
mutable std::shared_timed_mutex mutex_;
unsigned int value_ = 0;
};
int main() {
ThreadSafeCounter counter;
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.emplace_back( {
for (int j = 0; j < 100; ++j) {
counter.increment();
}
});
}
for (auto& t : threads) {
t.join();
}
std::cout << "Final value: " << counter.get() << std::endl;
return 0;
}
12. 字面量后缀
C++14 扩展了字面量后缀,增加了时间字面量和字符串字面量。
标准库字面量
cpp
#include <chrono>
#include <string>
#include <string_view>
using namespace std::literals;
// 时间字面量
auto duration1 = 100ms; // 100毫秒
auto duration2 = 5h; // 5小时
auto duration3 = 1.5min; // 1.5分钟
// 字符串字面量
auto str1 = "hello"s; // std::string
auto str2 = "world"sv; // std::string_view (C++17)
用户定义字面量
cpp
// 用户定义字面量
constexpr long double operator"" _km(long double x) {
return x * 1000.0; // 将公里转换为米
}
constexpr long double operator"" _pi(long double x) {
return x * 3.14159265358979323846L;
}
int main() {
auto distance = 5.0_km; // 相当于 5000.0L
auto angle = 2.0_pi; // 相当于 6.28318530717958647692L
return 0;
}