当前位置: 首页 > news >正文

建设ipv6网站wordpress缓存怎么清理

建设ipv6网站,wordpress缓存怎么清理,建设工程质量检测网站,中国建筑网最新招聘构造函数模板推导 它允许编译器推导类模板的参数类型#xff0c;无需程序员显式指定。这带来了编写模板代码时的便利性#xff0c;使得代码更加简洁。在此之前#xff0c;创建模板类的对象时必须显式指定模板参数#xff0c;即使这些参数可以从构造函数的参数中推导出来。…构造函数模板推导 它允许编译器推导类模板的参数类型无需程序员显式指定。这带来了编写模板代码时的便利性使得代码更加简洁。在此之前创建模板类的对象时必须显式指定模板参数即使这些参数可以从构造函数的参数中推导出来。 在C17之前如果我们有如下模板类 template typename T class MyClass { public:MyClass(T value) : value_(value) {}private:T value_; };构造一个该模板类的对象我们必须这样写 MyClassint myObject(10); // 显式指定模板类型参数为int但是在C17中可以这样 MyClass myObject(10); // 编译器自动推导模板参数为int编译器能够根据传给构造函数的参数类型推导出类模板的类型参数。这就是构造函数模板推导。 注意事项 1. 默认构造函数的影响如果一个类模板定义了默认构造函数CTAD可能不会按预期工作。当试图利用CTAD时若没有提供足够的上下文以推断模板参数编译器可能无法确定正确的模板参数类型。 2. 复制和移动构造函数不参与模板推导C标准规定类模板的复制和移动构造函数不用于CTAD。当通过复制或移动另一个实例来构造一个对象时必须显式地指定模板参数。 3. 部分模板参数推导C17的CTAD不支持部分模板参数推导。如果一个类模板接受多个模板参数要么所有参数都由CTAD推导得出要么所有参数都必须显式指定。 结构化绑定 结构化绑定Structured Bindings允许将对象的多个成员绑定到一组变量上从而方便地一次性解包多个返回值或对象的多个成员。这种方法提高了代码的可读性和便捷性尤其在处理 tuple、pair 或结构体时特别有用。 在C17之前访问tuple或结构体的元素需要使用std::getindex或者通过成员访问操作符.如下所示 std::pairint, double p std::make_pair(1, 3.14); int x p.first; double y p.second;std::tupleint, double, std::string t std::make_tuple(1, 3.14, example); int a std::get0(t); double b std::get1(t); std::string c std::get2(t);而在C17中可以使用结构化绑定简化这个过程 auto [x, y] std::make_pair(1, 3.14); auto [a, b, c] std::make_tuple(1, 3.14, example);这样x和y被绑定到了pair的两个元素上a、b和c被绑定到了tuple的三个元素上。另外结构化绑定也可以用于数组和结构体 int arr[] {1, 2, 3}; auto [x, y, z] arr;struct MyStruct {int i;double d;std::string s; };MyStruct ms {1, 3.14, example}; auto [i, d, s] ms;结构化绑定如何帮助简化访问 tuple 和 pair 的过程 1. 减少代码量 在C17之前访问tuple或pair中的元素需要使用std::get函数或直接访问成员变量对于pair。 2. 提高代码可读性 使用结构化绑定后可以直接将tuple或pair的元素分配给多个变量如下 auto [num, text] std::make_pair(1, one); auto [i, d, s] std::make_tuple(1, 3.14, example);3. 类型推导 结构化绑定还能自动推导出绑定到各个变量的正确类型避免了显示类型指定的需要或潜在的类型不匹配错误。例如不需要显式声明num为int或text为std::string编译器会自动根据tuple或pair中的元素类型进行推导。 注意事项 非公有成员无法绑定如果尝试对一个结构体进行结构化绑定那么这个结构体的所有成员都必须是公有的。对于包含私有或受保护成员的结构体结构化绑定将无法编译通过。 数组的引用绑定尽管可以对数组使用结构化绑定但绑定的变量是数组元素的副本而非引用。这意味着对绑定变量的修改不会影响原始数组。 无法直接应用于动态数组结构化绑定不能直接应用于动态分配的数组或是它们的指针。它主要应用于静态分配的数组、STL容器例如std::array以及其他可被分解为已知数量元素的类型。 需要可解构的类型结构化绑定要求被绑定的类型是可解构的即类型需要有相应的std::tuple_size和std::get的特化或自定义的解构方式。对于没有这样特性的类型将不能使用结构化绑定。 无法用于类型转换结构化绑定不支持任何形式的类型转换。即不允许在绑定的同时进行类型强制转换。 只能用于声明结构化绑定必须在变量声明的同时使用不能用于已声明的变量。 无法指定修饰符对于结构化绑定的变量不能单独为其中的变量指定修饰符如const或volatile。所有变量的修饰符都必须与结构化绑定的整体修饰符一致。 无法用于修改结构或类类型的访问规则对于结构体或类的成员结构化绑定遵循原有的访问控制规则。即使通过结构化绑定创建了成员的别名也不能绕过成员的访问权限。 if-switch语句初始化 C17之前通常将在if或switch语句中用于条件判断的变量声明在这些语句之前。这种方式的缺点是任何在if或switch语句外声明的变量都会在整个作用域内保持活跃状态即使其只被用于条件判断。这可能会导致作用域污染因为这些变量可能会被后续的代码段不正确地访问或修改。为了限制变量的作用域开发者有时会使用额外的代码块来限定变量的范围。在C17中引入了if和switch语句的初始化器特性来解决这个问题。初始化器使得开发者能够在这些语句内部声明一个变量并且该变量的作用域被局限于该语句块及其控制的代码块中。 初始化器的工作原理主要基于两点 作用域变量在声明时会创建一个新的作用域这样变量的生命周期就被限定在if或switch语句及其相关的代码块内部。 语句执行流程if或switch语句的初始化器会首先执行然后才是条件判断。只有在初始化器执行后条件判断中使用的变量才被创建和初始化随后这些变量在相关条件分支的代码块中可用。 if语句初始化 在C17之前如果你想在if语句中使用一个只用于该判断逻辑的变量你通常需要这样写 auto val getValue(); if (val.isValid()) {// 使用val } // val 这里仍然是可见的即使我们可能不再需要它。在C17以后可以将初始化语句放在if语句中 if (auto val getValue(); val.isValid()) {// 使用val } // val 在这一作用域外不可见不会造成变量污染。这个特性使得你可以在条件判断的作用域内声明变量让代码更加紧凑且防止了变量的作用域外溢。 switch语句初始化 类似的对于switch语句C17之前你可能会写 auto result computeValue(); switch (result.type()) {case TypeA:// 处理TypeAbreak;case TypeB:// 处理TypeBbreak;// 更多情况... } // result 在这里仍然是可见的。而C17引入初始化语句后你可以这样写 switch (auto result computeValue(); result.type()) {case TypeA:// 处理TypeAbreak;case TypeB:// 处理TypeBbreak;// 更多情况... } // result 在这一作用域外不可见。注意事项 单一语句限制在if或switch的初始化部分只能包含一条语句。如果需要执行多条语句必须将它们封装到一个作用域块中或者使用逗号运算符来连接。 作用域限制初始化语句中声明的变量仅在if或switch语句的作用域内有效。这意味着一旦离开了该语句的作用域这些变量就无法访问了。虽然这有助于防止变量泄露到外部作用域但在某些情况下可能会限制变量的可用性。 条件必须存在使用初始化语法时必须有一个条件表达式跟随初始化声明。即使你只想要初始化而不需要条件判断也必须提供至少一个始终为真或假的条件。 对嵌套使用的限制在嵌套的if或switch语句中使用这个特性时最内层的条件判断只能访问最近的一个初始化变量。这限制了在嵌套结构中对外层初始化变量的访问有时可能需要更多额外的变量声明来规遍这个限制。 内联变量 这是一个针对全局变量和静态成员变量的新特性。该特性主要是为了解决程序中静态存储期的常量和变量的多个定义可能导致的链接问题。 在C17之前如果你在头文件中定义了一个非内联的静态成员变量或全局变量当该头文件被多个源文件包含时每个源文件都会产生该变量的一个实例。链接时会因为找到多个相同符号的定义而导致错误。 解决这个问题传统上的做法是在头文件中声明变量使用extern而在一个单独的源文件中定义它。这样可以保证变量只定义一次不会引起链接冲突。 // header.h extern int globalVariable; // 声明// source.cpp int globalVariable; // 定义但是对于模板和常量表达式这样做不太方便因为你可能想要它们在头文件中即被定义以便内联。 使用C17的内联变量特性后你可以在头文件中安全地定义全局变量或静态成员变量编译器会保证在整个程序中这个变量只有一个实例。即使头文件被多个源文件包含也不会产生链接错误。 // header.h inline int globalVariable 42; // 内联定义// 或者对于类的静态成员 class MyClass { public:static inline int staticMember 42; // 内联定义 };内联变量的一个常见用途是定义类模板的静态数据成员。在C17之前这通常需要特别的定义在一个源文件中而现在可以直接在类定义中内联定义它们这样每个模板实例化都会得到正确的链接。 templatetypename T class MyClass { public:static inline T staticMember; // 内联定义 };内联变量的原理是通过内联修饰符将变量定义为可在多个编译单元通常是多个.cpp源文件中共享的而不会违反一次定义规则One Definition Rule, ODR。当内联变量在头文件中定义时这个定义可以在包含了该头文件的每个编译单元中见到。 这里的关键是链接器(linker)如何处理内联变量 链接器与内联变量 链接器的作用是将多个编译单元合并到一起形成一个可执行程序。对于内联变量链接器将遵守以下规则 弱符号内联变量的每个实例在编译时都会被编译器标记为“弱符号”Weak Symbol。这表示该符号可能会在其他编译单元中有重复定义链接器在链接的过程中需要处理这些定义。 合并符号当链接器发现多个弱符号定义例如来自不同编译单元的内联变量的实例时它会将这些符号的实例合并成一个单一的实例。这种处理方式保证了程序的所有部分看到的内联变量只有一个共同的实体。 清除多余的实例通过合并符号的方式链接器会避免在最终的程序中包含重复的变量定义因此程序不会因为违反ODR而出错。 内存中的表现 内联变量被存储在程序的数据段类似于其他全局变量。在运行时无论内联变量在程序的哪个地方被访问它总是指向同一个内存位置。这就是为什么你可以在一个头文件中定义内联变量并在多个源文件中使用它却不会造成链接错误或多份拷贝的原因。 注意事项 初始化位置的限制根据C17的规定内联变量必须在声明它的地点同时进行定义。这意味着与普通的外部链接变量可以在声明时不定义不同内联变量在声明的同时需要提供其初始化器。 非内联情景尽管内联变量主要用于解决头文件中的变量多重定义问题但它们并不适用于所有需要内部链接的情况。对于在单个翻译单元内使用的局部静态变量使用普通的静态变量而不是内联变量通常更合适。 与静态成员变量的兼容性虽然内联变量特别适用于类的静态成员定义但是对于不需要跨多个翻译单元共享的静态成员变量使用传统的静态成员变量定义方法在类定义外定义和初始化静态成员可能更为直接。 缺乏动态初始化顺序保证和其他全局对象一样内联变量的动态初始化即运行时初始化顺序在不同翻译单元之间是不确定的。这意味着如果你的程序依赖于两个全局内联变量的初始化顺序那么在不同的编译环境中可能会遇到问题。 折叠表达式 一个与可变参数模板一起使用的特性允许对包中的参数执行一个包含二元运算符的递归展开。 可变参数模板可以接受任意数量的模板参数但在C11和C14中处理它们通常需要递归模板或递归函数。这样的代码不仅写起来复杂阅读起来也很困难。折叠表达式提供了一种更简洁和直接的方式来处理可变模板参数。 折叠表达式的种类 C17 提供了两种形式的折叠表达式 二元右折Binary Right Fold: (... op pack)其中op是二元运算符pack是模板参数包或函数参数包。展开后会变成(pack1 op (... (packN-1 op packN)))。 二元左折Binary Left Fold: (pack op ...) 展开后会变成 ((pack1 op pack2) op ...) op packN。 折叠表达式的使用 示例使用折叠表达式实现可变参数模板的和。 templatetypename... Args auto sum(Args... args) {return (... args); // 右折叠 }int main() {auto total sum(1, 2, 3, 4); // 返回 10 计算过程是1 (2 (3 4)) }折叠表达式对所有内建的二元运算符有效例如, -, *, /, , ||, , |等等并且当它们被使用时你可以省去编写明确的递归模板实例化代码直接将参数包中的元素应用到相应的运算符上。 示例使用折叠表达式实现逻辑与。 templatetypename... Args bool logicalAnd(Args... args) {return (... args); // 右折叠 }int main() {bool result logicalAnd(true, true, false, true); // 返回 false 计算过程是 true (true (false true)) }注意事项 操作符的限制折叠表达式可以使用大部分C的二元操作符但并非所有操作符都支持。特殊的操作符如成员访问符.、成员指针访问符-、下标操作符[]、函数调用操作符())并不能直接用于折叠表达式。若需要使用这些操作可能需要结合其他技术如Lambda表达式。 只适用于可变参数模板折叠表达式仅适用于模板参数包无法将其用于非模板的可变参数函数或其他上下文中的参数列表。 空参数包的处理对于空的模板参数包折叠表达式需要特殊处理因为没有直接的元素可以参与运算。在这种情况下需要为折叠表达式提供一个显式的初始化值或者使用特定的操作符这些操作符在展开空包时有明确定义的行为例如逻辑与展开为true逻辑或||展开为false。 推导和模板参数类型由于折叠表达式在编译时展开编译器必须能够推导出所有参与操作的参数类型。对于一些复杂的情况如参数类型不一致或需要隐式类型转换可能会导致推导失败或产生意外的结果。 constexpr lambda表达式 在C17中constexpr关键字的应用范围得到了极大的扩展包括对lambda表达式的支持。这一特性使得lambda表达式能在编译时被求值进一步拓展了常量表达式和编译时计算的应用场景。 当一个lambda表达式被标记为constexpr时编译器会尝试在编译时对这个lambda表达式进行求值前提是所有的输入也都是编译时可知的常量表达式。这样的lambda表达式可以用于任何需要常量表达式的场景比如模板参数、数组大小、编译时断言等。 注意事项 函数体中的操作限制 constexpr函数体内只能包含有限的操作类型。这主要包括返回语句、类型定义、使用constexpr变量、条件语句如if和switch、循环如forwhile以及对其他constexpr函数的调用。其中对循环的使用在C20中才正式支持C17对此有较为严格的限制。 不允许使用动态内存分配 在constexpr函数中不能使用new或delete操作符因为这些操作涉及到运行时的内存分配和释放而constexpr函数必须在编译时完成计算。 不允许有副作用 constexpr函数不应包含改变函数外部状态的副作用例如修改非局部变量、进行输入输出操作或调用非constexpr函数等。 限制递归深度 尽管C17标准中没有明确指出递归深度的限制但编译器可能会对constexpr函数的递归深度做一定的限制以避免编译时资源耗尽。 声明与定义 如果一个constexpr函数被声明在某个头文件中则其定义也必须与之一同出现在同一头文件中以确保在编译时可以查到其定义。 虚函数限制 在C17中虚函数不能被声明为constexpr。这是因为虚函数主要用于运行时多态而constexpr函数必须在编译时就能够确定其行为。 返回类型和参数 constexpr函数的返回类型以及所有参数类型必须是字面类型。字面类型包括算术类型、枚举、指针、引用以及某些类类型这些类型在编译时必须有已知的固定大小并且能够在编译期构造和析构。 namespace嵌套 嵌套命名空间定义nested namespace definition允许开发者以更简洁的方式定义嵌套命名空间。这项特性通过减少代码冗余提高了代码的可读性和书写效率。 嵌套命名空间的简化语法并不影响C的编译器如何处理命名空间。在编译时这种简化的写法会被展开为传统的多层嵌套方式因此它不会影响命名空间的作用域规则、名字查找或者链接规则。 实质上嵌套命名空间的简化语法是一种语法糖意味着它给程序员提供了一种更为便捷和直观的书写方式而在编译器处理时它与传统的长格式写法在逻辑上是等价的。 旧式嵌套命名空间的书写方式 在C17之前定义嵌套的命名空间 namespace A {namespace B {namespace C {// 在这里定义或声明C中的内容}} }这种方式在定义深层嵌套的命名空间时会导致代码逐渐右移增加了代码的视觉嵌套深度。 C17中嵌套命名空间的书写方式 C嵌套命名空间定义 namespace A::B::C {// 在这里定义或声明C中的内容 }这种新语法允许你通过使用单列的::运算符来定义多层嵌套的命名空间大幅降低了代码的嵌套深度使得代码更加整洁。 注意事项 不允许局部嵌套命名空间嵌套命名空间的定义语法不能在函数内部或任何局部作用域内部使用。它们只能在全局作用域或其他命名空间内使用。 不支持命名空间别名定义中使用不能在命名空间别名namespace alias的定义中使用嵌套命名空间的语法。例如namespace XYZ A::B::C; 是有效的但不能在这种别名定义中进一步引入嵌套。 匿名命名空间匿名命名空间通常用于定义仅在当前文件中可见的类型或变量不能与嵌套命名空间的新语法结合使用。匿名命名空间需要被单独声明。 __has_include预处理表达式 预处理表达式__has_include这是一个条件编译特性用于检查编译环境中是否存在指定的头文件或模块。 原理详解 __has_include是一个编译时预处理指令其工作原理如下 检查提供的路径__has_include接收一个指定的头文件或模块路径作为参数。这个路径可以是标准库的头文件使用header也可以是用户定义的头文件使用header。 编译器搜索在预处理阶段编译器会在其配置的搜索路径中查找指定的文件。这些路径包括了编译器的内建路径、通过命令行参数指定的路径以及源码中指定的路径。 返回检查结果如果编译器找到了指定的文件__has_include表达式的结果为true实际上就是1否则为false即0。这个结果可以被用在预处理命令#if、#elif中从而影响条件编译的流程。 基本用法 __has_include预处理表达式接受一个头文件名或模块名作为参数以判断该文件或模块是否可被包含或导入。基本语法如下 #if __has_include(header_or_module)#include header_or_module #else// 备选方案 #endif或者是针对用户定义的头文件 #if __has_include(my_header.h)#include my_header.h #else// 备选方案 #endif注意事项 1. 不能确定文件内容 __has_include仅能检查指定的头文件或模块是否存在于编译器的搜索路径中但不能保证文件的内容符合期望。例如即使检测到某个库的头文件存在也无法确定该库是否实现了你所需要的特定特性或API。 2. 预处理器的局限 __has_include是一个预处理器指令这意味着它的执行在编译之前。由于预处理器只能进行简单的文本替换和条件编译__has_include无法访问或评估C代码中的类型、模板或函数等元素。这限制了其使用场景主要局限于对编译环境的适配上。 在lambda表达式用*this捕获对象副本 这一改进增加了lambda表达式的灵活性并提供了一种更加直观和安全的方式来使用当前对象的成员变量和成员函数特别是在异步编程和多线程环境下。 *this捕获的引入 对象的复制当使用*this捕获时编译器将创建当前对象的一个副本。这个副本是通过调用当前对象的拷贝构造函数生成的。这意味着在lambda表达式内部使用的是这个副本而不是原始对象。 封装到lambda体内编译器将这个副本封装到生成的闭包类即转换后的lambda表达式内部。每个通过*this捕获的lambda表达式都会拥有当前对象的一个唯一副本。这保证了即使原始对象的生命周期结束lambda表达式也能安全地操作副本上的数据避免了悬挂指针或未定义行为的发生。 简化异步编程和多线程使用在需长时间运行或在不确定原始对象生命周期的环境中使用*this捕获确保了操作的对象在lambda执行期间始终有效大大降低了编程的复杂性和出错率。 传统的捕获方式 在C17之前在lambda表达式中访问类的成员变量或成员函数通常有两个选择 通过[this]捕获这会捕获当前对象的指针然后在lambda表达式中通过this访问成员。通过[]或[]捕获通过值或引用捕获所有外部自动变量包括this然后在lambda表达式中通过对象的成员访问它们。 这两种方式都有潜在的风险。特别是在涉及到异步调用或者长时间运行的操作时原始对象可能在lambda表达式执行时已经不再存在这可能导致对悬挂指针的访问或未定义行为。 使用*this捕获的好处 C17通过允许在lambda表达式中使用*this来捕获当前对象的副本当lambda表达式被执行时它将使用一个当前对象的副本而不是直接访问原始对象。这有几个显著的好处 避免悬挂指针由于lambda表达式持有当前对象的副本而非原始对象的引用或指针所以即使原始对象被销毁lambda表达式内部的操作仍然是安全的。值捕获的直观性使用*this明确表明你想通过值而非通过指针或引用来捕获当前对象增加了代码的明确性和可读性。易于异步编程在异步编程和多线程应用中确保操作的对象在执行期间有效且不会改变是非常重要的。通过*this捕获可以简化这一点的管理。 使用示例 考虑一个类该类有一个成员变量和一个成员函数我们希望在成员函数中创建一个lambda表达式捕获当前对象的副本并在稍后执行 class MyClass { public:int value 0;void doSomething() {// 使用*this捕获当前对象的副本auto lambda [*this]() {// 这里可以安全地使用value因为我们操作的是副本std::cout value std::endl;};// 在某个异步操作或延迟执行lambda...} };在这个示例中不论原始的MyClass对象何时析构lambda表达式内部的value访问都是安全的因为它操作的是一个独立的副本。 注意事项 拷贝构造函数的要求当使用*this来捕获对象副本时必须保证该对象是可拷贝的。即对象需要有一个可访问的拷贝构造函数。如果类的拷贝构造函数被删除例如通过 delete或未定义那么使用*this捕获会导致编译错误。 副本与原始对象的独立性捕获的副本在修改时不会影响到原始对象。这虽然在多数情形下是期望的行为但如果存在某些场景需要对原始对象进行操作这种独立性可能导致不符合预期的结果。在这种情况下应考虑其他捕获方式或设计。 继承和多态性由于捕获的是对象的副本该副本的类型将严格对应于*this的静态类型这意味着如果在带有继承关系的类中使用*this进行捕获捕获的对象副本将不会保持任何多态性质。这可能会在处理基于类层次结构的系统时引入限制。 新增Attribute 属性是被方括号[[ ]]包围的注解可以应用于代码中几乎任何地方包括类型声明、语句、表达式等用于提供关于代码行为的额外信息给编译器。在C11和C14中已经引入了部分属性C17则进一步扩展了这个概念。 [[nodiscard]] [[nodiscard]]属性可以用来指示函数的返回值不应该被忽略。当编译器检测到一个被标记为[[nodiscard]]的函数的返回值没有被使用时它将发出警告。这对于那些执行重要任务其返回值表明操作成功与否的函数特别有用确保了程序逻辑的正确性。 示例 [[nodiscard]] int computeValue() {return 42; }void example() {computeValue(); // 假设没有使用返回值编译器可能会警告 }[[maybe_unused]] 在某些情况下变量、函数、类型等可能声明了但未被使用这通常会引发编译器警告。通过使用[[maybe_unused]]属性可以告诉编译器该实体可能不被使用从而抑制出现相关警告。 示例 [[maybe_unused]] void unusedFunction() {// Implementation }[[fallthrough]] 在switch语句中一般认为每个case后都应该跟随一个break来阻止代码继续向下“跌落”。如果某个case故意设计为需要“跌落”到下一个case[[fallthrough]]属性就可以用来消除编译器针对这种有意为之的情况所发出的警告。 示例 switch (value) {case 1:doSomethingForCase1();[[fallthrough]]; // 明确指出意图跌落case 2:doSomethingForCase2();break; }[[noreturn]] [[noreturn]]属性指示函数不会通过正常返回来返回到调用者。这主要用于那些通过抛出异常或终止程序来“返回”的函数。 示例 [[noreturn]] void fatalError(const std::string message) {std::cerr message;std::exit(1); }注意事项 编译器支持度虽然这些属性是C17标准的一部分不同的编译器对这些新特性的支持程度可能有所不同。一些较旧的编译器版本可能不完全支持C17的所有属性。 误用和滥用 [[nodiscard]]如果过度使用几乎所有函数都标记为[[nodiscard]]可能会导致代码中充满警告从而降低此属性对于关键函数返回值检查的实际作用和意图。[[maybe_unused]]应该只在确实预期到某些变量或函数在某些条件下不被使用时使用滥用可能会隐藏实际的代码质量问题。[[fallthrough]]仅应在确实希望在switch语句中无条件“跌落”到下一个case时使用错误使用会掩盖潜在的逻辑错误。 运行时影响为零这些属性是在编译时处理的它们不会改变程序的运行时行为。需要注意的是虽然它们可以帮助改进代码的质量和避免某些类型的错误但它们并不替代良好的编程实践和代码审查。 字符串转换 对字符串与数值类型之间转换的增强特别是通过std::from_chars和std::to_chars这两个函数模板。这些功能是在头文件charconv中定义的它们提供了一种高效的方式来将数值转换为字符串形式以及将字符串解析为数值类型比之前的解决方案如std::stoi、std::stof、std::to_string等提供了更好的性能和更多控制。 std::from_chars std::from_chars是用于将字符串转换为数值的函数。与std::stoi或std::stol等相比std::from_chars提供了无异常、低开销的解析方法。 #include charconv #include iostreamint main() {const char* str 1234;int value 0;auto [ptr, ec] std::from_chars(str, str std::strlen(str), value);if(ec std::errc()) {std::cout Parsed value: value std::endl; // 输出: Parsed value: 1234}return 0; }std::to_chars 与std::from_chars相对应std::to_chars用于将数值转换为字符串。与传统的std::to_string相比std::to_chars提供了更好的控制和性能特别是不产生内存分配。 #include charconv #include iostream #include arrayint main() {std::arraychar, 10 str;int value 1234;auto [ptr, ec] std::to_chars(str.data(), str.data() str.size(), value);if(ec std::errc()) {std::string result(str.data(), ptr); // ptr 指向最后一个字符的下一个位置std::cout Converted string: result std::endl; // 输出: Converted string: 1234}return 0; }std::variant 提供了一种类型安全的方式来存储和访问一个值这个值可以是预先定义类型列表中的任何类型。这使得std::variant成为处理多种类型数据的理想选择特别是在不确定应该使用哪种类型时。 类型安全的联合体 std::variant基本上是一个更加安全、更灵活的联合体union。与传统的联合体不同std::variant会自动管理存储的类型保证任何时候只有一种类型的值被正确地存储。 自动生成的成员函数 std::variant自动生成默认构造函数、拷贝构造函数、移动构造函数、赋值操作符等这些都是基于其持有的类型的相应函数安全地执行的。 访问存储的值 可以通过std::get函数使用编译时的类型安全检查来访问std::variant中的值。如果请求的类型与当前存储的类型不匹配将会抛出std::bad_variant_access异常。 std::holds_alternative用于在运行时检查std::variant是否存储了特定类型的值。 访问与访问者模式 std::visit允许对std::variant存储的值应用函数或函数对象。这是使用访问者模式的一种强大方式可以基于存储的值类型执行不同的操作而不需要显式地检查其类型。 #include variant #include iostreamstruct PrintVisitor {void operator()(int i) const { std::cout int: i \n; }void operator()(float f) const { std::cout float: f \n; }void operator()(const std::string s) const { std::cout string: s \n; } };int main() {std::variantint, float, std::string v Hello, std::variant!;std::visit(PrintVisitor{}, v); }std::variant的递归和间接使用 std::variant支持存储类型为std::shared_ptr或std::unique_ptr的情况使其可以间接地存储同一std::variant类型从而实现递归数据结构。 注意事项 大小限制std::variant的大小至少等于其可保存类型中最大类型的大小加上类型信息的管理需要的额外空间。这意味着std::variant可能会比存储单一类型的变量消耗更多内存空间。 异常处理在通过std::getT访问std::variant中错误类型的值时会抛出std::bad_variant_access异常。这要求开发者必须小心处理访问逻辑尤其是在不能使用异常的环境中如一些嵌入式系统使用std::variant可能会变得复杂。 不支持引用类型std::variant不能直接存储引用类型。虽然可以通过使用std::reference_wrapper间接支持引用但这增加了使用复杂性。 递归类型需要特殊处理直接使用std::variant定义递归类型例如一个树节点可能包含一个子节点列表这些子节点也是相同类型是不可能的。通常需要通过std::shared_ptr来间接实现。 std::optional 提供了一种表达可选optional语义的方式。使用 std::optional 可以表示一个变量可能持有某种类型的值或者不持有任何值。std::optional 主要用于处理那些可能不存在值的情况从而避免了使用原始指针或特殊值来表达“空”或“无效”值的需要。 存储 std::optional 内部通过联合体union来存储其包含的值。联合体是一种特殊的数据结构它可以存储不同的数据类型但在任何给定时刻只能包含其中一种类型的值。std::optional 使用这种结构来选择性地存储其包装的值类型 T。为了处理 T 类型对象的构造和析构std::optional 在其联合体内同时管理一个表示存储状态即值是否存在的布尔标志。 构造和析构 当 std::optional 被构造并包含一个值时它会在其内部联合体中构造这个值并设置存在标志为 true。当 std::optional 被析构或被赋予一个新的值或无值时如果当前存储了一个值则会调用这个值的析构函数并更新存在标志。 值访问 当访问 std::optional 存储的值时std::optional 提供了 value 和 operator* 方法。这些方法首先检查存在标志确认是否有值可以返回。如果尝试访问一个不存在的值std::optional 将抛出 std::bad_optional_access 异常。 状态检查 std::optional 提供 has_value 方法来检查其是否包含一个值。这直接返回内部的存在标志。 与引用类型交互 虽然 std::optional 不能直接存储引用类型但可以通过 std::reference_wrapper 来包装引用使得 std::optionalstd::reference_wrapperT变为可能的从而在一定程度上模拟对引用的可选性。 类型转换 std::optional 提供了条件显式转换操作符允许 std::optionalT 在需要 bool 类型的上下文中被隐式使用使得可以直接在条件语句中使用。 实现示例 下面是一个简化的 std::optional 实现示例展示了一些核心概念 templatetypename T class Optional { private:alignas(T) unsigned char storage[sizeof(T)];bool hasValue;public:Optional(): hasValue(false) {}Optional(const T value) {new (storage) T(value); // 在 storage 上就地构造 T 类型对象hasValue true;}~Optional() {if (hasValue) {reinterpret_castT*(storage)-~T(); // 调用析构函数}}bool has_value() const { return hasValue; }T value() {if (!hasValue) throw std::bad_optional_access{};return *reinterpret_castT*(storage);} };注意事项 使用 value() 方法时如果 std::optional 不含值将抛出 std::bad_optional_access 异常。这要求调用者必须准备好处理这种潜在的异常情况或者在访问值前通过 has_value() 检查状态以避免异常。 std::optional 不直接支持引用类型。尽管这并非完全是限制但它要求开发者使用如 std::reference_wrapper 等工具来间接实现对引用的支持这增加了使用的复杂度。 当 std::optional 包含一个对象时该对象处于完全构造的状态当 std::optional 为空时表示不含任何对象。在这两种状态之间转换需要注意对象的构造和析构特别是需要关注所含对象的生命周期和资源管理。 std::any 用于在单个变量中存储任意类型的值。与其他语言中的类似构造相比C 的 std::any 提供了类型安全的方式来处理不确定类型的值同时依旧保持了静态类型语言的特性和优势。 主要特性 类型安全即使 std::any 可以存储任意类型的值但在访问这些值时必须明确指定正确的类型。尝试以错误的类型访问 std::any 中的值将导致抛出 std::bad_any_cast 异常。 灵活性std::any 可以用来存储任何拷贝构造的类型从简单的基本类型到复杂的自定义类型都行这使得它在处理不同类型的数据时非常灵活。 容易使用std::any 提供了简单的 API比如 any_cast用于安全地从 std::any 对象中提取值。 使用场景 std::any 常见的使用场景包括但不限于 函数返回多种类型当函数可能返回不同的类型时std::any 可以用作返回类型从而使得函数更加灵活。 容器存储不同类型的对象在需要在同一个容器中存储不同类型对象时std::any 提供了一种类型安全的解决方案。 API 和库开发在创建可用于不同上下文和带有不同类型参数的 API 或库时std::any 可以用来处理那些在编写时未知的类型。 基本用法 #include iostream #include any #include stringint main() {std::any a 10; // 存储 intstd::any b std::string(Hello, world!); // 存储 string// 尝试提取值try {std::cout std::any_castint(a) std::endl;std::cout std::any_caststd::string(b) std::endl;// 错误的类型抛出 bad_any_caststd::cout std::any_castfloat(a) std::endl; } catch (const std::bad_any_cast e) {std::cout e.what() std::endl;}// 检查是否有值if (a.has_value()) {std::cout a has a value. std::endl;}// 重置 a使其不再包含任何值a.reset();if (!a.has_value()) {std::cout a now has no value. std::endl;}return 0; }在这个例子中我们创建了两个 std::any 对象它们分别存储了一个 int 和一个 std::string。然后我们使用 std::any_cast 尝试提取存储的值并且展示了如何正确处理类型不匹配的情况。 注意事项 尽管 std::any 提供了以类型安全的方式存放和访问任意类型的能力但这个特性依赖于运行时检查。因此在尝试使用 std::any_cast 提取值时如果类型不匹配将抛出 std::bad_any_cast 异常。这要求开发者必须非常确信他们知道存储在 std::any 中的实际类型或已准备好处理可能的异常。 与直接使用具体类型相比std::any 内部实现可能涉及到动态内存分配尤其是当存储的类型大小超过一个小对象优化阈值时并且类型擦除和运行时类型检查会引入额外的性能成本。这可能不适合对性能有严格要求的应用。 std::any 不能直接存储引用类型。尽管可以通过包装如 std::reference_wrapper 来间接实现这一点但这增加了使用复杂度并且不如直接持有引用直观。 不支持不可拷贝构造的类型std::any 要求存储的类型必须是拷贝构造的这意味着对于无法拷贝构造的类型例如具有删除拷贝构造函数的类型std::any 不能使用。 类型检查仅在运行时 虽然 std::any 引入了一种处理类型不确定性的手段但类型的安全性检查仅限于运行时。 std::apply 允许你将一个元组的各个元素作为参数应用于某个函数或可调用对象。这提供了一种便利的方式来动态地以元组形式存储参数然后在需要时解包unpack这些参数并调用函数。 std::apply 实现的核心是利用模板递归和参数包展开unpack机制来将元组中的每个元素作为参数传给一个函数或可调用对象。这个过程涉及到了模板编程和一些 C11 开始引入的高级特性如变参模板和参数包。 在底层std::apply 的实现通常会涉及到几个关键步骤 1. 索引序列的生成 为了能够在编译时处理元组中的每个元素std::apply 需要一种机制来遍历元组的每个元素。C14 引入的 std::index_sequence 和 std::make_index_sequence 就是专门用于这种情况的工具它们可以生成一个编译时常量序列这个序列可以用作模板参数以索引访问元组中的每个元素。 2. 模板递归和参数包展开 通过获取元组的大小来生成一个对应的 std::index_sequence 之后std::apply 会利用变参模板和参数包的特性通过模板递归来遍历这个序列并对元组中的每个元素进行访问。 具体来说std::apply 会定义一个辅助模板函数这个函数接受一个函数或可调用对象、一个元组和一个索引序列。通过模板展开它会使用 std::get 来按索引依次获取元组中的每个元素并传递给目标函数。 3. 完成调用 当所有元素都被准备好后就可以直接将它们作为参数调用目标函数了。这一过程完全在编译时完成不涉及运行时类型检查或者类型转换因此是类型安全的。 基本用法 下面是 std::apply 的一个基础示例展示如何使用它来将一个元组的元素作为参数传递给函数 #include iostream #include tuple #include utility// 一个简单的函数用于演示 void print(int a, const std::string b, float c) {std::cout a , b , c std::endl; }int main() {// 创建一个元组存放将要传递给函数的参数auto arguments std::make_tuple(1, Hello, 3.14f);// 使用 std::apply 将元组的元素解包作为参数传递给 print 函数std::apply(print, arguments);return 0; }在上面的示例中std::apply 接受了一个函数print和一个元组arguments然后将元组中的元素作为参数传递给了 print 函数。 注意事项 std::apply 要求被调用的函数或可调用对象的参数列表与元组中的类型完全匹配。如果类型不完全一致比如参数类型不能自动转换为目标函数所需的类型会导致编译错误。 由于元组本身存储的是值即使它们是引用的包装器如 std::refstd::apply 不能直接用来传递引用参数除非通过一些特殊手段例如使用 std::reference_wrapper。 无法用于非拷贝构造类型由于元组本质上存储的是值如果你尝试在元组中存储一个不可拷贝构造的类型比如某些封装了资源管理的类可能会遇到问题。std::apply 自身并不限制这一点但元组的这一特性限制了无法直接存储非拷贝构造的类型。 std::make_from_tuple 实用特性允许你从一个元组tuple中直接构造对象。这种能力特别有用于场景其中对象的构造函数参数被存储或转发为一个元组而需要从这个元组中构造对象。 基本用法 std::make_from_tuple 需要 #include 头文件并可以如下使用 #include tuple #include string #include iostreamstruct MyStruct {int x;std::string y; };int main() {// 创建一个元组用作构造MyStruct的参数auto t std::make_tuple(10, Hello World);// 使用std::make_from_tuple从元组t构造MyStruct的实例MyStruct s std::make_from_tupleMyStruct(t);std::cout s.x , s.y std::endl; // 输出: 10, Hello Worldreturn 0; }注意事项 std::make_from_tuple 试图使用元组中的值来构造一个对象对象的构造函数的参数类型和数量必须与元组中的元素相匹配。如果没有匹配的构造函数代码将无法编译。这要求构造函数必须可以接受元组中提供的所有参数。 每个元组中的元素类型必须与目标对象构造函数中对应参数的类型精确匹配或者至少能够隐式转换。如果类型间不兼容导致不能隐式转换那么构造过程将失败。 处理引用和指针时需要小心。因为 std::make_from_tuple 直接按值传递元组内的元素到构造函数如果目标构造函数期望一个引用或指针作为参数那么元组中相应的元素也必须是引用或指针否则可能不会按预期工作。 如果目标对象的构造函数需要的是一个非拷贝和非移动类型的参数std::make_from_tuple 可能无法正常使用。由于元组内的元素在传递给构造函数时本质上是被拷贝或移动的如果你的类型不能拷贝或移动那么将不能使用 std::make_from_tuple。 std::string_view 提供一种轻量级、非拥有的字符串访问方式。std::string_view 代表了对字符序列的视图即查看或访问字符序列的方式它并不拥有这些字符序列而是提供了对原始字符串数据的引用。 特性 无需拷贝 使用 std::string_view 可以避免在字符串操作中进行不必要的拷贝。例如提取子字符串或传递字符串到函数中时不需要创建字符串的副本。非拥有性 std::string_view 只是提供对字符串或任何字符序列的视图它不拥有字符串的数据因此不负责管理数据的生命周期。灵活性 它能够以统一的方式访问不同类型的字符串或字符数组包括 std::string、字符串字面量、字符数组等。高效的字符串操作 提供了一系列用于字符串处理的方法如 substr、find、compare 等这些操作通常比 std::string 更高效因为它们不需要创建字符串副本。 使用示例 以下是使用 std::string_view 的一个简单示例 #include iostream #include string_view #include stringint main() {std::string str Hello, world!;std::string_view sv(str);// 输出原始字符串std::cout sv std::endl; // 输出子字符串std::cout sv.substr(7) std::endl; // world!// 查找字符串size_t pos sv.find(,);if (pos ! std::string_view::npos) {std::cout Comma found at position: pos std::endl;} else {std::cout Comma not found std::endl;}return 0; }注意事项 由于 std::string_view 不拥有其所引用的字符串数据因此需要保证在 std::string_view 的生命周期内其底层的字符串数据仍然有效且未被修改。如果底层数据被销毁或超出作用域通过 std::string_view 访问这些数据将导致未定义的行为可能会引发程序崩溃或数据损坏。 std::string_view 主要设计为只读视图它不提供直接修改字符串内容的功能。如果需要修改字符串你需要操作原始字符串或基于 std::string_view 的内容创建一个新的 std::string 实例进行修改。 std::string_view 可能并不保证引用的字符串是以空字符(‘\0’)终止的尤其是当它引用字符串的一部分如通过 substr 方法创建的视图时。这意味着不能直接将 std::string_view 用于那些期望C风格字符串即空终止字符串的API如某些C标准库函数。 as_const 获取对象的常量引用。这个功能使得在需要常量引用的地方能够轻松从非常量对象获得其常量引用从而避免不必要的对象复制同时也确保了对象内容不会被修改。 功能概述 std::as_const 函数位于头文件 utility 中。它接受一个引用作为参数并返回参数的const类型引用。这个函数非常简单但在需要确保不修改传入对象的情境下非常有用。 使用场景 std::as_const 最常见的用途之一是在调用需要常量引用参数的函数时确保传递的对象不会被函数修改。此外也可以用它来避免重载函数时引发的歧义。 注意事项 std::as_const 要求其参数必须是左值因为它返回一个对原始对象的引用。尝试将 std::as_const 用于临时对象非左值将引发编译错误因为这会产生一个对临时对象的悬挂引用是危险的且未定义的行为。 当你需要修改对象时自然不能使用 std::as_const因为它返回的是对象的常量引用。这意味着它仅适用于那些需要保障不修改对象的场景。 std::as_const 不会改变对象的生命周期。它返回的常量引用与原始对象共享相同的生命期。因此使用 std::as_const 时需要确保原始对象的生命周期至少与其返回的引用一样长。如果原始对象先于其引用销毁那么引用将成为悬挂引用访问它将导致未定义行为。 std::as_const 仅用于添加 const 限定符。它不进行任何类型转换或其他任何操作。如果需要转换类型比如进行静态转换static_cast或动态转换dynamic_cast需要单独进行。 file_system filesystem 头文件是文件系统库的一部分提供了一组用于操作文件系统的类和函数。这标志着 C 对文件和路径操作的现代化和标准化。在这之前文件系统的操作依赖于平台特定的API或第三方库而 filesystem 库的引入旨在提供一种跨平台、统一且易于使用的解决方案。 主要特性 路径操作 std::filesystem::path 类提供了一个灵活的方式来构建和操作文件路径无论是绝对路径还是相对路径支持路径的拼接、解析等操作。 文件和目录的检查 提供了诸如 exists(), is_directory(), is_empty(), is_regular_file() 等函数可用于检查文件或目录的状态和属性。 文件和目录的创建与删除 支持创建和删除目录create_directory(), remove(), remove_all() 等以及移动或重命名文件和目录rename()。 文件大小和文件最后修改时间 可以通过 file_size() 获取文件大小通过 last_write_time() 获取文件的最后修改时间。 目录遍历 std::filesystem::directory_iterator 和 std::filesystem::recursive_directory_iterator 提供了遍历目录和子目录的能力使得文件的搜索和处理变得更加简单。 文件权限 支持检查和修改文件权限例如只读、可执行等。 空间信息 可以获取磁盘或文件系统的空间信息如总空间、可用空间和空闲空间space()。 示例代码 以下是一个简单的示例展示如何使用 filesystem 库查询当前工作目录并列出其中的文件 #include iostream #include filesystemnamespace fs std::filesystem;int main() {fs::path current_dir fs::current_path(); // 获取当前工作目录std::cout Current directory: current_dir std::endl;std::cout Files in directory: std::endl;for (const auto entry : fs::directory_iterator(current_dir)) {std::cout entry.path() std::endl;}return 0; }注意事项 符号链接和循环引用在处理符号链接时filesystem 库的某些操作可能导致意外的行为例如无限循环在递归目录时。 不适用于所有类型的文件系统 filesystem 库主要设计用于常见的磁盘文件系统可能不适用于特殊类型的文件系统如网络文件系统或虚拟文件系统。 std::shared_mutex 一个互斥锁特别适合于“读多写少”的场景。与 std::mutex 相比std::shared_mutex 提供了更加灵活的锁定策略支持共享锁定多个读者和排他锁定单个写者。 基本特性 共享锁定Shared Locking允许多个线程同时对数据进行读取操作。当数据被共享锁定时任何尝试进行排他锁定的操作都将被阻塞直到所有的共享锁都被释放。 排他锁定Exclusive Locking仅允许一个线程进行写操作或独占访问。当数据被排他锁定时任何其他的排他锁定或共享锁定请求都将被阻塞直到排他锁被释放。 与 std::mutex 和 std::shared_timed_mutex 的比较 std::mutex提供基本的互斥锁定不区分共享和排他锁定任何时候只允许一个线程持有锁。std::shared_timed_mutexC14 引入除了支持共享和排他锁定外还提供了带有超时的锁定操作。std::shared_mutex 在 C17 中被加入作为一个不支持超时操作的简化版本提供更高的性能。 使用场景 std::shared_mutex 特别适用于访问模式为“读多写少”的场景比如缓存系统其中数据的读取操作远多于更新操作。使用 std::shared_mutex 可以显著提高这类应用的并发性能因为它允许多个读者线程同时安全地访问数据而无需等待其他读者线程释放锁。 示例代码 #include shared_mutex #include iostream #include thread #include vectorstd::shared_mutex mutex; int shared_data 0;void reader(int id) {std::shared_lockstd::shared_mutex lock(mutex);// 安全地读取 shared_datastd::cout Reader id sees shared_data shared_data std::endl; }void writer(int id) {std::unique_lockstd::shared_mutex lock(mutex);// 安全地修改 shared_datashared_data;std::cout Writer id updated shared_data to shared_data std::endl; }int main() {std::vectorstd::thread readers;for(int i 0; i 5; i) {readers.push_back(std::thread(reader, i));}std::vectorstd::thread writers;for(int i 0; i 2; i) {writers.push_back(std::thread(writer, i));}for(auto reader_thread : readers) {reader_thread.join();}for(auto writer_thread : writers) {writer_thread.join();}return 0; }此代码示例创建了几个读者和写者线程展示了如何通过 std::shared_mutex 安全地对共享数据进行读写操作。 ———————————————————————————————————————————————————————————— 【大厂面经、学习笔记、实战项目、大纲路线、讲解视频 领取文档】C校招实习、社招、面试题 https://docs.qq.com/doc/DR2N4d25LRG1leU9Q C/CLinux服务器开发/高级架构师学习资料包括C/CLinuxgolang技术NginxZeroMQMySQLRedisfastdfsMongoDBZK流媒体CDNP2PK8SDockerTCP/IP协程DPDKffmpeg等领取君羊739729163 合理利用自己每一分每一秒的时间来学习提升自己不要再用没有时间“来掩饰自己思想上的懒惰趁年轻使劲拼给未来的自己一个交代
http://www.zqtcl.cn/news/720460/

相关文章:

  • 哪里有网站推广软件免费推广seo策略方法
  • 阿里云备案网站 网站名称怎么写京icp备案查询
  • 网站开发岗位思维导图alexa排名
  • 自适应网站建设济南济南网站建设公司
  • 巴州网站建设库尔勒网站建设钟爱网络杭州微信网站制作
  • 52做网站南京市住房城乡建设门户网站
  • 网站开发精品课程贵阳市白云区官方网站
  • seo整站优化服务会计培训班一般收费多少
  • 批量网站访问检测怎么做好手机网站开发
  • 深圳网站建设公司哪家比较好shortcodes wordpress
  • 网站内链越多越好嘛可以做3d电影网站
  • 企业网站需求文档微商引流客源最快的方法
  • 交互式网站备案业务网站在线生成
  • 自建网站百度个人网站如何在百度上做推广
  • 如何安装wordpress模板竞价网站做seo
  • 做论坛网站如何赚钱电子商务营销推广
  • 想要自己做一个网站怎么做济宁百度网站建设
  • 海会网络建设网站wordpress刷不出图片
  • 一个人做商城网站网站推广的几个阶段
  • 做国学类网站合法吗html5教程pdf下载
  • 云南省文化馆网站建设二级域名分发平台
  • 网站版面布局结构图网站收录批量查询
  • 网站开发手机模拟器常州到丹阳
  • 淮南医院网站建设班级网站开发报告
  • 东莞营销网站建设哪家好微信api接口
  • 凡科建站怎么导出网页wordpress视频采集插件
  • 个人介绍网站源码云主机上传网站
  • app推广平台网站系统登录入口
  • 做公司宣传册的网站成crm网
  • 新乡公司做网站军事新闻内容摘抄