C++ 17 std::any
std::any 可以保存任意类型的单个值。和 std::variant 不同,variant 的类型集合是在编译期固定好的,而 any 更灵活,但类型信息也更弱。
1. std::any 是干什么的?它适合这种场景:
你需要一个通用容器
里面可能装不同类型的值
但这些类型在编译期不一定能全部列出来
例如插件配置、扩展参数、弱类型数据通道等。
2. 基本使用1234567891011121314#include <any>#include <iostream>#include <string>using namespace std;int main(){ std::any data = 10; cout << any_cast<int>(data) << endl; data = string("hello"); cout << any_cast<string>(data) << endl ...
C++ 17 std::optional
std::optional 用来表示“一个值可能存在,也可能不存在”。它很适合替代那些用特殊值、空指针或者额外布尔标记来表示“无结果”的老写法。
1. 为什么需要 std::optional?以前一个函数如果可能取不到值,常见写法一般是:
返回特殊值,比如 -1
通过输出参数返回
返回指针或 nullptr
这些方式都能工作,但表达力不够直接。std::optional<int> 一眼就能看出:这个函数返回的是“可能为空的 int”。
2. 基本使用1234567891011121314151617181920#include <optional>#include <iostream>using namespace std;optional<int> findValue(bool ok){ if (ok) { return 100; } return nullopt;}int main(){ auto r ...
C++ 17 std::variant
std::variant 是 C++17 提供的一个类型安全联合体(type-safe union)。它可以在多个类型中保存其中一个值,但和传统 union 不同的是,它知道自己当前存的是哪一种类型。
1. 为什么需要 std::variant?以前如果一个变量可能是多种类型,常见做法一般有几种:
用 union
用基类指针 + 多态
自己维护一个“类型标签 + 数据”的结构
这些方法都能解决问题,但要么不够安全,要么写起来比较重。std::variant 可以把“多种可能类型中的一种”这个需求直接表达出来。
2. 基本使用2.1 定义 variant1234567891011#include <variant>#include <iostream>using namespace std;int main(){ std::variant<int, double, string> data; data = 10; cout << get<int>(data ...
C++ 17 字符串转换
提到 C++17 的字符串转换,最值得记住的是 <charconv> 头文件里的 std::to_chars 和 std::from_chars。它们提供了一种更轻量、更高性能的数值与字符序列转换方式。
1. 为什么还需要新的字符串转换接口?在这之前,我们常见的做法有:
std::stoi
std::stol
std::to_string
stringstream
这些接口都能完成任务,但也有一些问题:
有的依赖区域设置(locale)
有的性能一般
有的会抛异常
stringstream 用起来偏重
而 to_chars / from_chars 的目标就是提供一个更底层、更直接的转换接口。
2. std::to_charsstd::to_chars 用于把数值写入字符缓冲区。
基本示例1234567891011121314151617#include <charconv>#include <array>#include <iostream>using namespace std;int main() ...
C++ 17 新增Attribute
Attribute 可以理解成“附加给编译器看的标注信息”。C++17 在这方面新增了几个很常用的属性:[[nodiscard]]、[[maybe_unused]] 和 [[fallthrough]]。它们不会改变程序核心逻辑,但能帮助编译器给出更合理的诊断。
1. 什么是 Attribute?它的基本写法是:
1[[attribute_name]]
这类标记通常用于告诉编译器:
这个返回值最好别忽略
这个变量没用是有意为之
这个 switch 的 case 故意不写 break
所以它更偏向代码语义提示和静态检查增强。
2. [[nodiscard]]这个属性的作用是:提醒调用者不要忽略返回值。
示例12345678910[[nodiscard]] int getValue(){ return 42;}int main(){ getValue(); return 0;}
上面这种调用方式,编译器通常会给出“返回值被忽略”的警告。
适合场景
错误码返回
资源申请结果
状态判断函数
...
在lambda表达式用*this捕获对象副本
C++17 允许在 lambda 捕获列表中使用 *this。它的含义不是捕获 this 指针,而是按值捕获当前对象的一个副本。这个特性主要用来解决 lambda 延迟执行时,原对象已经变化甚至已经销毁带来的问题。
1. 先看 this 捕获是什么在类成员函数里,lambda 很常见:
12345678910111213class Test{public: void func() { auto f = [this]() { value++; }; }private: int value = 0;};
这里捕获的是 this 指针。也就是说,lambda 里面访问的仍然是原对象本身。
2. *this 又是什么?[*this] 则不同,它会把当前对象拷贝一份到 lambda 里。
123auto f = [*this]() { return value;};
这样 lambda ...
C++ 17 __has_include预处理表达式
__has_include 是 C++17 标准化的一个预处理特性,用来判断某个头文件是否存在。它特别适合做兼容性处理,比如“有这个头文件就包含,没有就走备用方案”。
1. 为什么需要它?以前做跨平台或者兼容不同编译环境时,经常会遇到这样的问题:
某些标准库头文件在旧环境里没有
某些第三方库只在部分机器安装了
同一功能在不同平台的头文件名字不一样
如果没有 __has_include,这类判断通常只能依赖构建系统或者手工配置。有了它之后,预处理阶段就能直接判断头文件是否存在。
2. 基本语法123#if __has_include(<optional>)#include <optional>#endif
或者:
123#if __has_include("my_config.h")#include "my_config.h"#endif
它既支持尖括号形式,也支持双引号形式。
3. 最简单的示例1234567891011121314#include <i ...
C++ 17 namespace嵌套
C++17 给嵌套命名空间带来的改动非常简单,但很实用:可以把多层 namespace 合并成一行来写。它不是什么大功能升级,不过确实能让代码更整洁。
1. 以前怎么写?在 C++17 之前,如果要定义多层命名空间,通常这样写:
1234567891011namespace project {namespace module {namespace detail {void func(){}}}}
逻辑不复杂,但闭合大括号看起来有点多,层级深的时候也比较烦。
2. C++17 的新写法从 C++17 开始,可以直接写成:
1234567namespace project::module::detail {void func(){}}
功能完全一样,但代码明显更简洁。
3. 基本示例1234567891011121314151617#include <iostream>using namespace std;name ...
1. C++98 标准的类成员初始化在C++98中,支持了在类声明中使用等号 = 加初始值 的方式,来初始化类中静态成员常量 。这种声明方式我们也称之为”就地”声明。而非静态成员变量的初始化则必须在构造函数中进行。
例如:
123456789101112struct Base { Base() : a(250) {} Base(int num) : a(num) {} int a; int b = 1; static int c = 0; static const double d = 3.14; static const char* const e = "i am luffy"; const static int f = 0; };
如果按照 C++98 标准来解读上面这段代码,其中有这么几行语法是错误的:
第7行:类的非静态成员,必须在构造函数中进行初始化
第8行:类的静态成员,必须在类的外部进行初始化
第9行:类的静态常量成员,但不是整形或者枚 ...
C++ 17 constexpr lambda表达式
从 C++17 开始,lambda 表达式可以在满足条件时被当作 constexpr 使用。这个特性让 lambda 不再只是运行期的小函数对象,在一些场景下它也能参与编译期计算。
1. 什么是 constexpr lambda?简单理解就是:如果一个 lambda 的函数体本身满足常量表达式要求,那么它就可以在编译期求值。
例如:
12345constexpr auto add = [](int a, int b) { return a + b;};constexpr int value = add(1, 2);
这里的 add(1, 2) 就可以在编译期直接算出结果。
2. C++17 之前和之后的区别在早期标准里,lambda 更多是运行时工具,虽然能写得很灵活,但不太适合做编译期运算。到了 C++17,这方面明显宽松了不少,很多简单 lambda 都能自然地变成常量表达式。
这对模板元编程、编译期辅助计算、以及一些需要 constexpr 回调的小场景都比较有帮助。
3. 基本示例3.1 最简单 ...










