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

重庆城乡住房建设厅网站网站开发 接个支付支付难吗

重庆城乡住房建设厅网站,网站开发 接个支付支付难吗,科技公司网页,电商平台系统开发文章目录 常用的c11新特性1.自动推导类型(auto)2.lambda表达式3.智能指针4.范围for循环5.右值引用 - 移动语义6.类型别名7.constexpr8.static_assert(静态断言)9.nullptr10.列表初始化11.继承构造函数12.显示虚函数重载(override)13.final14.变长模板参数15.新的容器与算法16.强… 文章目录 常用的c11新特性1.自动推导类型(auto)2.lambda表达式3.智能指针4.范围for循环5.右值引用 - 移动语义6.类型别名7.constexpr8.static_assert(静态断言)9.nullptr10.列表初始化11.继承构造函数12.显示虚函数重载(override)13.final14.变长模板参数15.新的容器与算法16.强类型枚举17.默认和删除函数18.委托构造函数19.并发库20.线程库 常用的c14新特性1. 通用lambda表达式2. 常量表达式3. 二进制字面量4. 数组大小自动推导5. std::make_unique6. std::exchange7. std::integer_sequence8. constexpr函数的扩展9. 变长参数模板的扩展 常用的c17特性1. 结构化绑定2. constexpr1. constexpr lambda2. constexpr if3. constexpr string 4. if with initializer5. std::optional6. inline7. std::filesystem8. 折叠表达式9. 模板的模板参数推导10. std::variant11. std::byte12. 并行算法 常用的c20新特性1. 模块(Modules)2. 协程(Coroutines)3. 概念(Concepts)4. 范围(Ranges)5.三向比较符(hree-way comparison)... 常用的c11新特性 在c推出的新特性中c11无疑是最具影响力的其次是c17与c14所以掌握好c11对于我们是很有必要 1.自动推导类型(auto) C11引入了auto关键字它用于推导变量的类型。使用auto可以使代码更简洁、更易于维护。 具体来说使用auto声明变量时编译器会根据变量的初始化表达式推导出变量的类型。例如 auto i 42; // 推导出i的类型为int auto d 3.14; // 推导出d的类型为double auto s hello; // 推导出s的类型为const char*auto还可以与迭代器结合使用例如 std::vectorint v {1, 2, 3, 4, 5}; for (auto it v.begin(); it ! v.end(); it) {std::cout *it ; }在这个例子中auto推导出了迭代器的类型使得代码更简洁。 需要注意的是auto不能用于函数参数、类成员和非静态局部变量的声明。此外auto推导出的类型是变量初始化表达式的类型而不是变量本身的类型。例如 int i 42; const auto r i; // 推导出r的类型为const int auto r2 i; // 推导出r2的类型为int auto x r; // 推导出x的类型为int在这个例子中auto推导出的类型分别是const int、int和int而不是int、const int和int。 2.lambda表达式 C11中的lambda表达式是一种匿名函数可以在代码中内联定义并立即使用。它的语法如下 [capture list] (parameter list) - return type { function body }其中capture list是一个可选的捕获列表用于捕获外部变量parameter list是函数参数列表return type是函数返回类型function body是函数体。 例如以下代码定义了一个lambda表达式它接受两个整数参数并返回它们的和 auto sum [](int a, int b) - int { return a b; };lambda表达式可以直接调用如下所示 int result sum(1, 2); // result为3lambda表达式的捕获列表可以用于捕获外部变量例如 int x 1; auto addX [x](int a) - int { return a x; };这个lambda表达式捕获了变量x并将其作为常量拷贝到函数体中。因此以下代码输出结果为3 std::cout addX(2) std::endl;lambda表达式还支持省略捕获列表、参数列表和返回类型例如 auto printHello [] { std::cout Hello! std::endl; }; printHello(); // 输出结果为Hello!3.智能指针 C11中引入了三种智能指针 unique_ptr独占式智能指针用于管理动态分配的对象。它禁止拷贝和赋值只能通过移动语义进行转移所有权从而保证在任何时候都只有一个unique_ptr指向同一个对象。 shared_ptr共享式智能指针用于多个指针共享同一个对象。它使用计数机制来记录有多少个指针指向同一个对象并在最后一个指针离开作用域时自动释放对象内存。 weak_ptr弱引用智能指针是一种特殊的共享式指针它不会增加对象的引用计数因此不会影响对象的生命周期。它主要用于解决shared_ptr的循环引用问题即两个对象相互持有shared_ptr指针导致无法释放内存的情况。通过使用weak_ptr可以打破这种循环引用避免内存泄漏问题。 这些智能指针都是通过RAII资源获取即初始化机制来管理内存可以避免手动管理内存带来的错误和麻烦。 4.范围for循环 C11中引入了范围for循环也称为foreach循环它提供了一种简单的方式来遍历容器中的元素不需要使用迭代器或手动控制循环变量。 范围for循环的语法如下 for (auto element : container) {// do something with element }其中container是一个容器例如数组、向量、列表、映射等element是容器中的一个元素auto表示自动推导类型并使用引用以便在循环中修改元素值。 使用范围for循环遍历容器时循环变量会依次指向容器中的每个元素直到遍历完所有元素为止。在循环体中可以对元素进行读取、修改或其他操作。 范围for循环的好处是代码更加简洁易读减少了手动控制循环变量的繁琐工作同时也避免了一些迭代器错误。 5.右值引用 - 移动语义 C11中的右值引用是一种新的引用类型用于表示临时对象或即将被销毁的对象。它的语法是在类型前添加双引号“”。 右值引用有两个主要的应用 移动语义 右值引用可以用于实现移动语义即将一个对象的资源如内存转移到另一个对象避免了复制和分配内存的开销。移动语义在C11中被广泛应用于STL容器和智能指针等类中。 完美转发 右值引用还可以用于完美转发即在函数调用时将参数以原始类型传递给另一个函数避免了复制和转换的开销。完美转发在泛型编程中非常有用可以实现更高效的代码。 需要注意的是右值引用只能绑定到右值不能绑定到左值。如果尝试将右值引用绑定到左值编译器将会报错。 以下是一个使用右值引用实现移动语义的例子 class MyString { public:MyString() : data(nullptr), len(0) {}MyString(const char* str) {len strlen(str);data new char[len 1];strcpy(data, str);}MyString(const MyString rhs) {len rhs.len;data new char[len 1];strcpy(data, rhs.data);}MyString(MyString rhs) {len rhs.len; //将资源全部转到新的后本身全部释放掉data rhs.data;rhs.len 0;rhs.data nullptr;}~MyString() {delete[] data;}private:char* data;size_t len; };int main() {MyString str1(hello);MyString str2(std::move(str1)); // 使用std::move将左值转成右值引用return 0; }在上面的例子中MyString类有一个移动构造函数它接受一个右值引用参数将其成员变量的值转移给新对象并将原对象的成员变量置为空。在main函数中我们使用’std::move将str1转换成右值引用然后将其传递给str2的移动构造函数从而实现了移动语义。 6.类型别名 C11中的类型别名是一种新的语言特性它允许程序员为一个已有的数据类型定义一个新的名字。这个新的名字可以用来替代原有的类型名并且可以使代码更加可读、易于理解。 类型别名的语法形式为 using NewTypeName OriginalTypeName;其中NewTypeName是新定义的类型名OriginalTypeName是原有的类型名。 例如我们可以定义一个类型别名来代替一个很长的类型名 using ComplexNumber std::complexdouble;这样我们就可以使用ComplexNumber来代替std::complex使代码更加简洁和易读。 类型别名还可以用来定义函数指针类型、模板类型参数等例如 using FuncPtr int(*)(int); templatetypename T using Vec std::vectorT;typedef std::complexdouble ComplexNumber;也可以使用typedef实现取别名,这些类型别名使得代码更加模块化和可读性更高同时也提高了代码的可维护性。 7.constexpr C11中的constexpr是一个关键字它用于指示编译器在编译时计算表达式的值并将其视为编译时常量。constexpr函数是一种在编译时计算结果的函数它可以用于在编译时进行优化提高程序的性能。 constexpr可以用于变量、函数和类成员函数。使用constexpr修饰的变量必须是一个常量表达式即在编译期间就可以确定其值的表达式。constexpr函数必须满足一些特定的限制如不能包含循环、递归、动态内存分配等。 constexpr可以用于简化代码并提高程序性能因为它可以在编译时计算表达式的值而不是在运行时计算。这意味着程序不需要在运行时执行表达式的计算从而提高了程序的性能。 总之C11中的constexpr是用于指示编译器在编译时计算表达式的值并将其视为编译时常量的关键字可以用于变量、函数和类成员函数并可以用于简化代码并提高程序性能。 8.static_assert(静态断言) C11中的static_assert是一个编译期断言compile-time assertion它可以在编译期间检查某个条件是否成立并在条件不成立时产生编译错误。它的语法如下 static_assert(condition, message);其中condition是需要检查的条件message是在条件不成立时产生的错误信息。如果condition为false则编译器会在编译期间产生一个编译错误并将message作为错误信息输出。 static_assert的作用是在编译期间检查某些重要的条件以避免在运行期间出现错误。例如可以使用static_assert检查模板参数的类型以确保只有特定类型的参数才能被使用。另外static_assert还可以用于检查编译器的特性是否支持某些特定的功能。 9.nullptr 在C11中nullptr是一个新的关键字代表一个空指针。在之前的C版本中通常使用0或NULL来表示空指针但这种方式有时会引起歧义因为0或NULL可能被解释为整数0而不是指针。 nullptr的引入解决了这个问题它是一个特殊的指针类型可以显式地表示空指针。使用nullptr可以使代码更加清晰明了避免了因为0或NULL被误解为整数而导致的错误。 例如在C11中可以使用nullptr来初始化指针 int *p nullptr;也可以将nullptr用作函数参数以明确表示函数需要一个空指针 void foo(int *p); foo(nullptr);总之nullptr是C11中一个非常有用的新特性可以提高代码的可读性和可维护性。 10.列表初始化 C11中的列表初始化是一种新的初始化语法可以使用花括号来初始化对象、数组和结构体等。它的语法形式为 T object {arg1, arg2, ...};其中T表示要初始化的对象类型object表示对象名称arg1、arg2等表示初始化参数。 列表初始化的优点是可以避免隐式类型转换和窄化转换从而在编译期就能够发现一些潜在的错误。例如 int x 1.2; // 隐式类型转换x的值为1 int y {1.2}; // 编译错误类型不匹配 int z {10000000000}; // 编译错误窄化转换除了可以用于基本类型的初始化列表初始化还可以用于STL容器和自定义类型的初始化例如 std::vectorint v {1, 2, 3}; std::mapstd::string, int m {{apple, 1}, {banana, 2}}; struct Point { int x; int y; }; Point p {1, 2};总之列表初始化是一种更加严格、更加安全的初始化方式可以在编译期就发现一些潜在的错误提高代码的健壮性和可维护性。 11.继承构造函数 C11中的继承构造函数是一种新特性它允许子类继承父类的构造函数从而简化了代码编写。 在C11中可以使用using语句来继承父类的构造函数。例如 class Base { public:Base(int a) {} };class Derived : public Base { public:using Base::Base; // 继承Base的构造函数 };在这个例子中Derived类继承了Base类的构造函数。使用using语句可以让Derived类使用Base类的构造函数从而避免了重复编写构造函数的麻烦。 此外继承构造函数还支持默认参数和模板参数。例如 class Base { public:templatetypename TBase(T a) {} };class Derived : public Base { public:using Base::Base; // 继承Base的构造函数 };int main() {Derived d(1); // 自动推导T为intreturn 0; }在这个例子中Base类的构造函数使用了模板参数而Derived类通过继承来自动推导模板参数从而实现了代码的简化。 总之继承构造函数是C11中的一个非常实用的新特性它可以让代码更加简洁、易读、易维护。 12.显示虚函数重载(override) C11中的显示虚函数重载是指在派生类中使用override关键字显式地重载基类中的虚函数。这样做的好处是可以提高代码的可读性和可维护性同时也可以帮助开发者在编译时捕获一些错误比如派生类中的函数名拼写错误或者参数类型错误等。 下面是一个使用override关键字的示例代码 class Base { public:virtual void foo(int x) {cout Base::foo(int) endl;} };class Derived : public Base { public:virtual void foo(int x) override {cout Derived::foo(int) endl;} };在这个示例代码中Derived类显式地重载了Base类中的虚函数foo并使用了override关键字。这样做的好处是当我们在Derived类中定义foo函数时如果函数名或者参数列表与Base类中的foo函数不一致编译器就会报错帮助我们在编译时就捕获这些错误。 需要注意的是使用override关键字只能用于虚函数的重载而不能用于非虚函数的重载。如果我们在Derived类中定义了一个与Base类中的非虚函数同名的函数编译器是不会报错的。 13.final 在C11中final是一个关键字用于修饰类、成员函数或虚函数表示它们不能被继承或覆盖。 在类的声明中final用于防止其他类继承该类。例如 class Base final {// ... };class Derived : public Base { // 错误Base类被声明为final不能被继承// ... };在成员函数或虚函数的声明中final用于防止其他函数覆盖该函数。例如 class Base { public:virtual void foo() final {// ...} };class Derived : public Base { public:void foo() override { // 错误Base类中的foo函数被声明为final不能被覆盖// ...} };通过使用final关键字可以避免子类修改父类的行为从而保护程序的正确性和可维护性。 14.变长模板参数 C11中的变长模板参数是一种允许模板接受可变数量参数的特性。其语法为在模板参数列表中使用“…”表示可变数量的参数称为“模板参数包”。使用变长模板参数可以使模板更加灵活和通用可以接受任意数量的参数而不需要为每种可能的情况都定义不同的模板。 例如下面是一个使用变长模板参数的示例 templatetypename... Args void print(Args... args) {(std::cout ... args) std::endl; }在这个例子中函数模板“print”接受任意数量的参数并将它们输出到标准输出流中。使用“…”表示模板参数包可以接受任意数量的参数。在函数体内使用“…”展开参数包将参数逐个输出到标准输出流中。 使用变长模板参数时还可以将参数包展开为函数参数列表或模板参数列表以实现更加复杂的功能。例如可以使用递归模板展开参数包以实现参数的逐个处理和转换。 总之变长模板参数是C11中非常有用的特性实现原理类似于递归可以使模板更加灵活和通用方便编写高效的泛型代码。 15.新的容器与算法 C11引入了一些新的容器和算法包括 unordered_map和unordered_set这些容器是基于哈希表实现的可以用于快速查找和插入元素。与map和set相比它们的插入、查找和删除操作都具有O(1)的平均时间复杂度。 array这是一个固定大小的数组容器可以在编译时确定大小。与普通数组相比它提供了更多的安全保障可以避免越界访问。 tuple这是一个元组容器可以包含多个不同类型的值。可以使用std::get函数来访问元组中的元素也可以使用std::tie函数将元组解包为多个变量。 move和forward这些是新的语言特性用于支持移动语义和完美转发。可以在容器和算法中提高性能和效率。 lambda表达式这是一个新的语言特性可以在代码中定义匿名函数。可以在算法中使用lambda表达式来提供自定义的比较函数或其他操作。 for_each和transform这些是新的算法可以在容器中对每个元素进行操作。for_each可以执行任意的操作而transform可以将容器中的元素进行转换。 sort和stable_sort这些是标准库中的排序算法可以对容器中的元素进行排序。sort使用快速排序算法而stable_sort使用归并排序算法并且保持相同元素的顺序不变。 总之C11引入了许多新的容器和算法可以提高代码的效率和可读性也可以简化代码的编写。 16.强类型枚举 C11中的强类型枚举是一种新的枚举类型它比传统的C枚举类型更加严格和安全。强类型枚举具有以下特点 枚举值的作用域限制在枚举类型中不会泄漏到外部作用域。枚举值可以指定基础类型比如int、char等。枚举值可以显式地指定数值而不是默认从0开始递增。 以下是一个强类型枚举的示例 enum class Color : int {RED 1,GREEN 2,BLUE 3 };Color c Color::RED; int n static_castint(c); // 显式转换为int类型在这个示例中我们定义了一个名为Color的枚举类型并指定了它的基础类型为int。枚举值RED、GREEN和BLUE的作用域限制在枚举类型Color中它们的数值分别为1、2和3。我们可以使用Color::RED来访问枚举值也可以使用static_cast将Color类型转换为int类型。 17.默认和删除函数 C11中引入了默认函数和删除函数的概念它们可以帮助我们更好地控制类的行为。 默认函数是指编译器自动生成的函数如果我们没有显式地定义这些函数编译器会自动为我们生成。C11中可以使用default关键字来显式地声明默认函数包括默认构造函数、拷贝构造函数、移动构造函数、拷贝赋值函数和移动赋值函数。 删除函数是指我们显式地告诉编译器不要生成某个函数。我们可以使用delete关键字来声明删除函数包括构造函数、拷贝构造函数、移动构造函数、拷贝赋值函数和移动赋值函数。这些函数的删除可以使得我们的代码更加清晰避免出现一些不必要的错误。 例如在以下的代码中我们使用default关键字来显式地声明了默认构造函数和拷贝构造函数同时使用delete关键字来删除拷贝赋值函数 class MyClass { public:MyClass() default;MyClass(const MyClass) default;MyClass operator(const MyClass) delete; };这样我们就可以防止对象被复制避免出现不必要的问题。 18.委托构造函数 委托构造函数是C11新增的特性它允许一个构造函数调用同一个类中的另一个构造函数从而避免代码重复。 委托构造函数的语法如下 class MyClass { public:MyClass(int a) { /* do something */ }MyClass(double b) : MyClass(static_castint(b)) { /* do something */ }MyClass(int a, double b) : MyClass(a) { /* do something */ } };在上面的代码中第二个构造函数使用了委托构造函数调用了同一个类中的第一个构造函数。第三个构造函数也使用了委托构造函数调用了同一个类中的第一个构造函数。 使用委托构造函数可以简化代码提高代码的可读性和可维护性。但需要注意的是委托构造函数必须放在构造函数的初始值列表中并且不能出现循环委托的情况。 19.并发库 C11引入了许多新的并发库以便更好地支持多线程编程。以下是其中一些重要的库 std::thread这个库提供了一个简单的API用于创建和管理线程。它允许线程并行执行以便更好地利用多核处理器。 std::mutex这个库提供了一种基本的同步机制用于保护共享资源。它允许多个线程访问共享资源但只允许一个线程访问该资源的任何时刻。 std::atomic这个库提供了原子操作用于保护共享变量。原子操作是一种特殊的操作可以保证在多个线程之间执行时不会出现竞态条件。 std::condition_variable这个库提供了一种同步机制用于等待某个条件的发生。它允许线程在等待某个事件发生时处于休眠状态并在事件发生时被唤醒。 std::future和std::promise这个库提供了一种异步编程模型用于处理异步操作的结果。它允许一个线程在另一个线程完成某个任务后获取结果。 std::async这个库提供了一种简单的方式用于在另一个线程中执行函数并在后台处理结果。它允许程序员以异步方式执行代码而无需编写复杂的线程管理代码。 这些并发库提供了一组强大的工具使C程序员能够更好地利用多核处理器和并发编程。 20.线程库 C11中的线程库是对多线程编程的支持。它提供了一组类和函数可以创建、启动和控制线程的执行。 以下是C11中线程库的一些主要类和函数 std::thread表示一个线程对象可以通过构造函数创建一个新的线程。 std::thread::join()等待线程执行完成然后结束线程。 std::thread::detach()分离线程使其在后台运行不会阻塞主线程。 std::mutex表示一个互斥锁用于保护共享资源。 std::lock_guard表示一个互斥锁的保护域用于自动管理互斥锁的加锁和解锁。 std::condition_variable表示一个条件变量用于线程之间的同步。 std::atomic表示一个原子变量用于多线程环境下的原子操作。 C11中的线程库提供了一种简单而有效的方法来实现多线程编程使得程序员可以轻松地创建和控制多个线程并在不同的线程之间共享数据。 常用的c14新特性 1. 通用lambda表达式 C14引入了通用lambda表达式可以使用auto关键字作为参数类型和返回类型使得lambda表达式更加灵活。 通用lambda表达式的语法如下 [ captures ] ( auto... params ) - decltype(auto) { body }其中captures是lambda表达式的捕获列表params是lambda表达式的参数列表decltype(auto)表示返回类型会根据body自动推导出来。 例如以下代码展示了一个使用通用lambda表达式的例子 #include iostreamint main() {auto add [](auto x, auto y) {return x y;};std::cout add(1, 2) std::endl; // 输出 3std::cout add(1.5, 2.5) std::endl; // 输出 4return 0; }在这个例子中lambda表达式的参数类型和返回类型都使用了auto关键字使得它可以接受不同类型的参数并返回相应的结果。 2. 常量表达式 C14中常量表达式是指在编译时可以计算出结果的表达式它可以用于声明常量、数组大小、枚举值等。 C14中新增了一些常量表达式的规则 函数可以被声明为常量表达式只要函数满足以下条件 函数的返回值类型是字面类型literal type函数体只包含符合常量表达式要求的语句 可以使用if和switch语句只要它们的条件表达式是常量表达式并且语句体也是符合常量表达式要求的语句。 可以使用循环语句只要循环次数是常量表达式。 可以使用lambda表达式只要它符合常量表达式的要求。 下面是一个使用常量表达式的例子 constexpr int factorial(int n) {return n 1 ? 1 : n * factorial(n - 1); }int main() {constexpr int n 5;int arr[factorial(n)]; // 使用常量表达式计算数组大小return 0; }在上面的例子中函数factorial被声明为常量表达式并用于计算数组arr的大小。由于n是一个编译时常量因此可以在编译时计算出factorial(n)的值从而确定数组的大小。 3. 二进制字面量 C14引入了二进制字面量允许程序员使用二进制表示法来表示整数值。 二进制字面量以前缀0b或0B开头后面跟着一串二进制数字。例如0b101010表示十进制数42。 以下是一个简单的示例代码 #include iostreamint main() {int a 0b101010;std::cout a std::endl; //输出42return 0; }二进制字面量提供了一种简单而方便的方法来表示位模式这对于编写低级别的系统代码或进行位运算非常有用。 4. 数组大小自动推导 在C14中可以使用auto关键字和初始化列表来实现数组大小的自动推导。具体来说可以使用以下语法 auto arr {1, 2, 3, 4}; // 自动推导为std::initializer_listint在这个例子中编译器会自动推导出arr的类型为std::initializer_list int 而数组的大小也会自动根据初始化列表的元素个数进行推导。因此上述代码等价于下面的代码 int arr[] {1, 2, 3, 4}; // 数组大小为4需要注意的是这种自动推导方式只适用于静态数组而对于动态数组来说还需要使用new运算符手动分配内存。另外由于std::initializer_list是一个轻量级的容器因此它的性能可能不如普通数组。 5. std::make_unique C14中的std::make_unique是一个函数模板用于创建一个std::unique_ptr对象并将其初始化为一个新对象。它接受一个可变参数列表和一个构造函数的参数列表用于在创建新对象时传递给构造函数。 make_unique的语法如下 templatetypename T, typename... Args std::unique_ptrT make_unique(Args... args);其中T是要创建的对象的类型Args是传递给构造函数的参数列表。make_unique返回一个std::unique_ptr对象该对象拥有指向新对象的所有权。 使用make_unique可以避免手动创建std::unique_ptr对象并进行new操作从而避免了内存泄漏和错误的可能性。它还可以提高代码的可读性和简洁性。 下面是一个使用make_unique创建一个动态分配的对象的例子 #include memory #include iostreamclass MyClass { public:MyClass(int value) : m_value(value) {std::cout MyClass constructor called with value: m_value std::endl;}~MyClass() {std::cout MyClass destructor called with value: m_value std::endl;} private:int m_value; };int main() {auto ptr std::make_uniqueMyClass(42);return 0; }在这个例子中我们使用make_unique创建了一个动态分配的MyClass对象并将其初始化为值42。当程序退出main函数时指向该对象的指针ptr将被自动销毁并调用MyClass的析构函数。 需要注意的是make_unique不能用于创建数组因为std::unique_ptr不支持动态数组。如果需要创建动态数组应该使用std::vector或std::array。 6. std::exchange std::exchange是C14中引入的一个函数模板它定义在头文件中。这个函数的作用是交换一个对象的值并返回其旧值。 std::exchange的函数原型如下 templateclass T, class U T T exchange(T obj, U new_value);其中T是要交换值的对象的类型obj是要交换值的对象的引用new_value是新值U是新值的类型。 这个函数的作用是将obj的值用new_value替换并返回obj原来的值。这个操作是原子的所以在多线程环境中使用是安全的。 下面是一个使用std::exchange的例子 #include iostream #include utilityint main() {int x 1;int y std::exchange(x, 2);std::cout x x , y y std::endl; //输出x 2, y 1return 0; }在这个例子中我们使用std::exchange将x的值从1替换成2并将原来的值1赋给了y。 7. std::integer_sequence C14中的std::integer_sequence是一个模板类用于创建一个整数序列。它可以用于编写与模板参数数量和类型无关的代码例如元编程和模板元函数。 std::integer_sequence有两个模板参数第一个是整数类型通常是std::size_t第二个是整数序列的长度。例如std::integer_sequencestd::size_t, 3表示一个包含三个std::size_t类型整数的序列。 std::make_integer_sequence模板函数可以用来创建一个整数序列。它接受一个整数类型和一个整数序列长度作为参数并返回一个std::integer_sequence对象。例如std::make_integer_sequencestd::size_t, 5将返回一个包含0到4的std::size_t类型整数的序列。 std::index_sequence是std::integer_sequence的特化版本其中第一个模板参数固定为std::size_t。它通常用于访问元组中的元素因为元组中的元素是按照索引顺序存储的。 使用std::integer_sequence和std::make_integer_sequence可以实现可变参数模板的参数展开例如 templatetypename... Ts void foo(Ts... args) {bar(std::make_index_sequencesizeof...(Ts){}, args...); }templatetypename... Ts, std::size_t... Is void bar(std::index_sequenceIs..., Ts... args) {// 访问args中的元素例如int x std::getIs(std::make_tuple(args...)); }在上面的例子中foo函数接受任意数量和类型的参数并将它们传递给bar函数。bar函数使用std::index_sequence来访问args中的元素。 #include iostream #include utilitytemplatetypename... Ts, std::size_t... Is void print_helper(const std::tupleTs... tpl, std::index_sequenceIs...) {((std::cout std::getIs(tpl) ), ...);std::cout \n; }templatetypename... Ts void print(Ts... args) {std::tupleTs... tpl(args...);print_helper(tpl, std::make_index_sequencesizeof...(Ts)()); }templatetypename... Ts void foo(Ts... args) {print(args...); }int main() {foo(1, 2.5, hello); // 输出1 2.5 helloreturn 0; }在上面的例子中print_helper函数使用std::index_sequence来展开std::tuple中的元素并将它们打印到控制台上。print函数创建一个std::tuple对象并使用std::make_index_sequence来创建一个std::index_sequence对象然后将它们传递给print_helper函数。foo函数使用print函数来打印参数。 8. constexpr函数的扩展 C14中对constexpr函数的扩展主要包括以下几个方面 放宽了对constexpr函数的限制在C11中constexpr函数只能包含一些简单的语句比如赋值语句和return语句而在C14中constexpr函数可以包含一些复杂的语句比如if语句和循环语句。 允许constexpr函数调用非constexpr函数在C11中constexpr函数只能调用其他constexpr函数而在C14中constexpr函数可以调用非constexpr函数只要这些函数的返回值可以在编译时确定。 允许constexpr函数返回void类型在C11中constexpr函数必须返回一个常量表达式而在C14中constexpr函数可以返回void类型只要函数体中的语句都是常量表达式。 允许constexpr函数有多个参数在C11中constexpr函数只能有一个参数而在C14中constexpr函数可以有多个参数只要这些参数都是常量表达式。 允许constexpr函数有局部变量在C11中constexpr函数不能有局部变量而在C14中constexpr函数可以有局部变量只要这些变量都是常量表达式。 总的来说C14中对constexpr函数的扩展使得这种函数更加灵活和实用可以用于更多的场景提高代码的可读性和可维护性。 9. 变长参数模板的扩展 C14中引入了变长参数模板的扩展可以使用类似于函数参数的语法来定义模板参数列表。这个特性被称为“参数包扩展”或“参数模板扩展”。 参数模板扩展允许在模板参数列表中使用省略号…来表示一个可变数量的模板参数。这些参数被称为“参数包”可以在模板定义中使用。 例如下面的代码定义了一个可变参数模板用于在编译时计算一组数字的总和 templatetypename... Args int sum(Args... args) {return (args ...); }在这个例子中省略号表示Args是一个可变数量的模板参数。在函数体中使用了折叠表达式fold expression来计算所有参数的总和。 使用参数模板扩展可以极大地简化代码特别是在处理不同数量的参数时。例如可以定义一个可变参数模板来打印任意数量的值 templatetypename... Args void print(Args... args) {(std::cout ... args) \n; }在这个例子中省略号表示Args是一个可变数量的模板参数。在函数体中使用了折叠表达式来将所有参数输出到标准输出。 常用的c17特性 注意当报错error C2429: 语言功能 XXX 需要编译器标志 /std:c17时需要再项目属性中选择c17标准 1. 结构化绑定 可以通过一行代码将结构体或元组中的成员绑定到变量上从而方便地访问这些成员。 struct Point {int x;int y; };Point p{1, 2}; auto [x, y] p;在这个例子中我们定义了一个Point结构体并创建了一个名为p的实例它包含了两个成员变量x和y。接着我们使用auto关键字和一对中括号将x和y变量绑定到了p的成员变量上。 在这之后我们就可以直接使用x和y变量来访问p的成员变量了而不需要通过p.x和p.y来访问。 需要注意的是只能在函数内部使用不能在全局作用域中使用结构化绑定仅适用于具有公共成员的结构体和元组类型。 此外结构化绑定还可以在for循环中使用从而方便地遍历数组、容器等数据结构中的元素。 std::vectorstd::pairint, std::string v{{1, one}, {2, two}, {3, three}}; for (auto [key, value] : v) {std::cout key: key , value: value std::endl; }在这个例子中我们定义了一个vector容器其中存储了多个pair类型的元素。在for循环中我们使用auto关键字和一对中括号将key和value变量绑定到了pair元素的first和second成员上。 在循环体中我们就可以直接使用key和value变量来访问pair元素的成员了。 需要注意的是结构化绑定在for循环中使用时变量名的顺序应该与元素成员的顺序一致否则会导致绑定错误。此外需要确保绑定的变量类型与元素成员的类型一致。 2. constexpr 1. constexpr lambda C17中的constexpr lambda可以用于编译时计算可以在编译时执行lambda表达式而不需要运行时执行。 #include iostreamint main() {constexpr auto square [](int x) { return x * x; };constexpr int result square(5);std::cout result std::endl;return 0; }在上面的示例中我们定义了一个constexpr lambda表达式它接受一个整数参数并返回该参数的平方。我们还定义了一个constexpr整数变量result它将square(5)的结果赋值给它。由于lambda表达式是constexpr的因此square(5)将在编译时计算而不是在运行时计算。 需要注意的是constexpr lambda表达式的参数和返回类型必须是字面类型否则无法在编译时计算。 2. constexpr if C17中的constexpr if是一种条件编译语句用于在编译时进行条件判断从而在编译期间选择不同的代码路径。 – 在if语句中使用constexpr关键字如果条件表达式是constexpr则编译器在编译期间进行判断。 – 如果条件表达式为真则编译器编译if语句中的代码块否则编译器忽略if语句中的代码块。 – 可以在if语句中使用else关键字来指定条件为假时要编译的代码块。 templatetypename T void print(const T t) {if constexpr (std::is_integral_vT){std::cout Integral type: t std::endl;}else if constexpr (std::is_floating_point_vT){std::cout Floating point type: t std::endl;}else{std::cout Unknown type: t std::endl;} }int main() {int i 42;float f 3.14f;std::string s hello;print(i); // 输出 Integral type: 42print(f); // 输出 Floating point type: 3.14print(s); // 输出 Unknown type: helloreturn 0; }在上面的示例代码中我们使用了constexpr if语句来根据传入的类型不同选择不同的输出方式。std::is_integral_v’是C17中的一个模板元编程工具用于判断一个类型是否为整数类型。它是一个类型特征type trait返回一个布尔值表示传入的类型是否为整数类型。std::is_floating_point_v’同理。 3. constexpr string C17中引入了std::string_view和constexpr std::string这两个新特性可以更方便地处理字符串。 constexpr std::string是一个编译时常量字符串可以在编译时计算不需要在运行时再次计算。可以使用以下方式创建 constexpr std::string str hello world;std::string_view是一个非拥有式的字符串视图可以用于访问字符串的子串。可以使用以下方式创建 std::string_view str_view hello world;非拥有式的字符串视图 它不拥有底层字符串的内存而是对底层字符串的一个引用或视图 。因此它不会在自身生命周期结束时释放底层字符串的内存。使用非拥有式的字符串视图可以避免复制字符串提高程序的性能。例如在函数调用中传递字符串参数时使用非拥有式的字符串视图可以避免不必要的内存复制。需要注意的是当使用非拥有式的字符串视图时需要确保底层字符串的生命周期不短于视图的生命周期否则可能会导致悬垂指针的问题。 4. if with initializer C17中的if with initializer是一种新的语法结构允许在if语句中声明和初始化变量。这种语法结构的好处是可以使代码更简洁因为变量声明和初始化可以在if语句中完成。 if (int x some_function()) {// 如果some_function返回的值不为0则进入if语句块// 变量x的作用域仅限于if语句块内部// 变量x的类型为int值为some_function()的返回值 }在上面的示例中if语句中声明了一个变量x并将其初始化为some_function()的返回值。如果some_function()的返回值不为0则进入if语句块。在if语句块之外变量x不再可见。 使用if with initializer可以避免在if语句之前声明变量并进行初始化的冗余代码。同时这种语法结构还可以使代码更加清晰和简洁。 5. std::optional std::optional是C17中引入的一个新特性用于表示可以存在或不存在的值。它类似于指针但提供了更好的语义和安全性。 #include iostream #include optionalstd::optionalint divide(int a, int b) {if (b 0) {return std::nullopt; // 表示不存在值} else {return a / b;} }int main() {auto result divide(10, 2);if (result) {std::cout Result: *result std::endl;} else {std::cout Error: Division by zero std::endl;}result divide(10, 0);if (result) {std::cout Result: *result std::endl;} else {std::cout Error: Division by zero std::endl;}return 0; }在这个例子中divide函数返回一个std::optional。如果除数为0则返回std::nullopt表示不存在值。否则返回a/b的结果。在main函数中我们使用if语句检查result是否存在值。如果存在则使用*运算符获取值并输出。否则输出错误信息。 std::optional还提供了其他一些有用的函数例如value_or函数用于获取值或默认值。例如 auto result divide(10, 0); int value result.value_or(-1); // 如果不存在值则返回-1std::optional是一个非常有用的工具可以避免许多指针相关的问题并提供更好的语义和安全性。 6. inline 在 C17 中可以使用 inline 关键字来定义内联变量。内联变量的定义必须在头文件中并且不能有初始化器。 // 头文件 example.h inline int x; // 声明内联变量// 源文件 example.cpp #include example.h int x 42; // 定义内联变量在使用内联变量时可以直接使用其名称就像使用普通变量一样#include example.h int main() {x 10; // 直接使用内联变量 xreturn 0; }需要注意的是内联变量的使用和内联函数的使用有些不同。内联函数的定义必须在每个使用它的编译单元中都可见否则会导致链接错误。而内联变量的定义只需要在任意一个编译单元中可见即可因为它们不会导致多个实例的生成。 7. std::filesystem C17中引入了std::filesystem库用于处理文件系统操作。 文件操作的函数很多我不在此举例可以自行查找文档。说一下相比于以前的文件操作的优势 更加简洁易用std::filesystem库提供了一组简洁易用的函数和类能够方便地完成常见的文件系统操作使代码更加简洁易读。 跨平台支持std::filesystem库能够跨平台支持各种操作系统包括Windows、Linux、macOS等操作系统因此可以在不同的平台上使用相同的代码。 更好的性能std::filesystem库的实现使用了现代操作系统的一些高效API能够更好地利用操作系统的缓存和异步I/O机制从而提高文件操作的性能。 更好的错误处理std::filesystem库提供了一组异常类能够更好地处理文件操作中可能出现的错误从而使代码更加健壮。 8. 折叠表达式 折叠表达式是C17中引入的新特性用于简化模板元编程和可变参数模板的实现。折叠表达式允许在编译时对一系列参数进行折叠操作最终得到一个值。 折叠表达式的语法如下 ( pack op ... op init ) ( init op ... op pack ) ( ... op pack ) ( pack op ... op )其中pack是可变参数列表中的参数op是操作符init是初始值。其中第三种和第四种语法需要至少有一个参数。 例如以下代码使用折叠表达式计算可变参数列表中的所有值之和 templatetypename... Args auto sum(Args... args) {return (args ...); // 折叠表达式 }int main() {int s sum(1, 2, 3, 4, 5); // s 15return 0; }在这个例子中(args ...)表示对所有可变参数进行求和操作。折叠表达式会将所有参数依次展开然后通过加法运算符对它们进行累加最终得到结果。 9. 模板的模板参数推导 在C17中类模板的模板参数推导被引入允许我们在使用类模板时省略模板参数列表中的一些参数而让编译器根据上下文自动推导。 从构造函数参数推导模板参数 templatetypename T class MyVector { public:MyVector(std::initializer_listT list) {// ...} };MyVector vec {1, 2, 3};在这个例子中我们没有显式指定MyVector的模板参数但编译器可以从std::initializer_list中的元素类型T推导出它的类型为MyVector。 从函数参数推导模板参数 templatetypename T class MyArray { public:T operator[](std::size_t index) {// ...} };void foo(MyArrayint arr) {// ... }MyArray arr {1, 2, 3}; foo(arr);在这个例子中我们也没有显式指定MyArray的模板参数但编译器可以从foo函数的参数类型MyArray推导出它的类型为MyArray。 需要注意的是类模板的模板参数推导只能用于构造函数和函数参数不能用于类模板的成员函数或者静态成员变量。另外模板参数推导只能用于单一的模板参数不能同时推导多个模板参数。 10. std::variant 学过QT的对这个关键字想必很熟悉C17中的std::variant是一种类型安全的联合类型它可以存储多个不同的类型值。在使用std::variant时需要包含头文件。 std::variant的声明方式如下 std::variantint, double, std::string v;这里声明了一个std::variant对象v它可以存储int、double和std::string类型的值。初始时v中没有值。 可以通过std::get函数从std::variant对象中获取值例如 v 3.14; double d std::getdouble(v);这里将3.14赋值给v然后通过std::get函数获取v中的double值并将其赋值给变量d。 当std::variant对象中存储的值类型与std::get函数传入的类型不匹配时将抛出std::bad_variant_access异常。 可以使用std::visit函数访问std::variant对象中的值。std::visit函数接受一个lambda表达式作为参数该lambda表达式的参数类型是std::variant对象中存储的所有类型。例如 std::visit([](auto arg) {std::cout arg std::endl; }, v);这里访问v中存储的值并将其输出到控制台。lambda表达式的参数类型是auto表示可以接受任意类型的参数。 std::variant还提供了一些其他的方法例如std::holds_alternative函数可以判断std::variant对象中是否存储了指定类型的值std::index_sequence_for函数可以获取std::variant模板参数中类型的数量等。 总的来说std::variant提供了一种灵活、类型安全的联合类型实现方式可以帮助我们更方便地处理多种不同类型的值。 11. std::byte c17中的std::byte是一个新类型用于表示字节数据。它是一种无符号整数类型有8个比特位可以表示0到255之间的值。与其他整数类型不同std::byte类型没有定义任何算术运算符因为它们不是数学上的对象而是表示二进制数据的字节。 使用std::byte类型可以更好地处理二进制数据因为它提供了更直观和类型安全的方式来表示字节。例如可以使用std::byte类型来读写二进制文件、网络数据包等。 #include iostream #include cstddefint main() {std::byte b1{0x12};std::byte b2{0xff};// 比较两个std::byte类型的值if (b1 b2) {std::cout b1 and b2 are equal std::endl;} else {std::cout b1 and b2 are not equal std::endl;}// 将std::byte类型转换为整数类型std::size_t n static_caststd::size_t(b1);std::cout n n std::endl;// 使用std::byte类型处理二进制数据std::byte buffer[1024];// 从文件中读取二进制数据到缓冲区// ...// 将缓冲区中的数据发送到网络// ... }12. 并行算法 C17中提供了一些新的并行算法可以使用这些算法来实现并行化的计算。这些算法都在头文件 execution 中定义使用前需要包含该头文件。 以下是使用C17并行算法的一些示例 std::for_each和std::for_each_n std::for_each和std::for_each_n可以用于并行地遍历一个序列对每个元素进行操作。 #include algorithm #include execution #include vectorint main() {std::vectorint v{1, 2, 3, 4, 5};// 并行遍历std::for_each(std::execution::par, v.begin(), v.end(), [](int x) {x * 2;});// 并行遍历前3个元素std::for_each_n(std::execution::par, v.begin(), 3, [](int x) {x * 2;}); }std::transform和std::transform_reduce std::transform可以用于并行地对一个序列进行变换操作std::transform_reduce可以用于并行地对一个序列进行变换操作并求和。 #include algorithm #include execution #include vectorint main() {std::vectorint v{1, 2, 3, 4, 5};// 并行变换std::vectorint result(v.size());std::transform(std::execution::par, v.begin(), v.end(), result.begin(), [](int x) {return x * 2;});// 并行变换并求和int sum std::transform_reduce(std::execution::par, v.begin(), v.end(), 0, std::plusint{}, [](int x) {return x * 2;}); }std::reduce和std::inclusive_scan std::reduce可以用于并行地对一个序列求和std::inclusive_scan可以用于并行地对一个序列进行前缀和。 #include algorithm #include execution #include vectorint main() {std::vectorint v{1, 2, 3, 4, 5};// 并行求和int sum std::reduce(std::execution::par, v.begin(), v.end());// 并行前缀和std::vectorint result(v.size());std::inclusive_scan(std::execution::par, v.begin(), v.end(), result.begin()); }std::sort和std::partial_sort std::sort可以用于并行地对一个序列进行排序std::partial_sort可以用于并行地对一个序列的前N个元素进行排序。 #include algorithm #include execution #include vectorint main() {std::vectorint v{5, 3, 1, 4, 2};// 并行排序std::sort(std::execution::par, v.begin(), v.end());// 并行部分排序std::partial_sort(std::execution::par, v.begin(), v.begin() 3, v.end()); }常用的c20新特性 1. 模块(Modules) C20中的模块(Modules)是用来取代头文件包含(include)的一种新的代码组织方式。模块可以提高编译速度,也增加了封装性。 使用模块的步骤如下: 创建模块接口文件(.ixx文件),定义要导出的接口: // hello.ixx export module hello;export void sayHello();创建模块实现文件(.cpp文件),实现接口: // hello.cpp module hello;void sayHello() {std::cout Hello World!\n; }在使用模块的文件中导入模块: // main.cpp import hello;int main() {sayHello(); }编译时需要添加-fmodules-ts参数。 模块的好处是可以明确定义公开的接口,也避免头文件包含导致的重复定义等问题。编译生成的二进制格式可以跨平台兼容。但模块目前还没有被所有编译器广泛支持 2. 协程(Coroutines) C20中引入了协程(Coroutines)的概念,可以用来实现一些异步任务。 协程的基本语法是: // 定义一个协程 coroutine返回值类型 协程名称();// 使用 co_await 语句暂停/恢复协程 co_await some_expression; // 协程最后需要一个 co_return 语句返回值 co_return some_value;一个示例: #include coroutinetaskint getValue() {int value co_await getValueFromNetwork();// do somethingco_return value; }int main() {auto result getValue(); // 不会堵塞auto value result.get(); // 获取协程返回值}在上面代码中,getValue() 被定义为一个协程,它可以通过co_await语句暂停自身,等待getValueFromNetwork()完成网络操作后再继续执行。 主函数调用getValue()只会启动协程任务,并不会堵塞,可以同时干其他事情。后面再通过result.get()来获取协程执行结束后的返回值。 这样就可以实现一种异步的编程模式了。 3. 概念(Concepts) C20中引入了概念(Concepts)来进行约束化的泛型编程。概念可以明确地指定模板参数需要满足的要求,使代码更清晰易懂。 定义一个概念的语法如下: template typename T concept MyConcept requires(T t) {t.someOperation(); };例1 - 限定类型必须是可increment的: concept Incrementable requires(T x) {x; // 必须支持运算 };template Incrementable T void incTwice(T num) {num;num; }incTwice(a); // 编译失败,如果a不支持例2 - 限定类型必须可比较: concept EqualityComparable requires(T a, T b) {{a b} - bool; // 必须可比较 };template EqualityComparable T bool isEqual(T a, T b) {return a b; }例3 - 使用嵌套约束: concept NestedConstraint EqualityComparable Incrementable; template NestedConstraint T void func(T x) {//... }例4 - 约束类的属性和行为: concept Drawable requires(T x) {x.draw(); // 必须有draw方法x.x; // 必须有x坐标x.y; // 必须有y坐标 };以前在C模板中,无法对传递进来的类型进行有效性检查,只能简单地使用或不使用,容易造成难以调试的错误, 概念允许我们明确地定义模板参数需要满足哪些条件,比如必须有某个成员函数,必须可转换为某种类型等,. 如果传入的类型不满足概念的要求,会直接导致编译错误,使问题更早暴露。 4. 范围(Ranges) C20中引入了范围(Ranges)的概念,可以简化数组、容器等的迭代操作。主要特点包括: 引入范围range的抽象概念,如数组、容器都可以视为一个range。统一开始和结束的迭代器概念为range的begin和end。算法可以直接作用于不同的range上,而不仅仅是容器。支持各种新的range Adaptor,可以将范围进行转换、过滤等。 下面是一个使用范围的简单例子: #include iostream #include ranges #include vectorint main() {std::vectorint vec {1, 2, 3, 4, 5}; // 用基于范围的for循环迭代for (int i : vec | std::views::filter([](int i){return i % 2 0;})) {std::cout i ; }// 输出:2 4 }这里通过views::filter() adapter将vec转换为一个过滤后的range,然后用range-based for循环进行迭代。 这种基于范围的编程方式可以大大简化迭代操作,而且可重用性强,非常适合函数式编程范式。 算法作用于范围 std::vectorint vec {3,1,4,5,2};std::ranges::sort(vec); for (int i : vec) {std::cout i ; } // 输出 1 2 3 4 5总之,范围大大简化了迭代器和算法的使用,是C现代化编程方式的重要进步。 5.三向比较符(hree-way comparison) C20 引入了 三向比较运算符,可以用来替代之前的 ,!,,,] 等二元比较运算符。 三向比较运算符按照下面的规则进行比较: 如果左边小于右边,返回 -1如果左边等于右边,返回 0如果左边大于右边,返回 1 一些使用三向比较运算符的示例: int a 1, b 2;if (a b -1) {// a b } if (a b 0) { // a b }if (a b 1) {// a b }std::sort(vec.begin(), vec.end(), [](int a, int b) {return a b; // 升序 });三向比较运算符提高了代码可读性,也统一了所有比较操作的方式。编译器可以更好地优化三向比较。 总体来说,三向比较使代码更简洁易读,也更符合现代C的编程思想,可以替代传统的二元比较运算符。 …
http://www.tj-hxxt.cn/news/140364.html

相关文章:

  • 17网做网站怎么建网站自己做赌场
  • 网站建设及维护价钱郑州做网站制作的公司
  • 成都建站网站模板sem代运营推广公司
  • 专业建设网站应该怎么做营销平台推广
  • 德宏企业网站建设公司微商城网站建设报价
  • 全球50个大网站开发语言抖音运营
  • 网络促销分类 网站促销模板网站建设公司
  • 恶意点击别人的网站erp网站建设
  • 申请网站域名wordpress 登录logo
  • 淮安做网站卓越凯欣做网站为什么需要购买域名
  • 网站正在建设中页面 英文dedecms5.7化妆品公司网站源码
  • 苏州网站建站推广网站开发的整体职业规划
  • 宁波网站建设流程wordpress 4.0 4.6
  • 长沙网站制作公司报价php网站是什么数据库文件
  • 有限公司网站入口网站登录页面模板
  • 江西锐安建设工程有限公司网站重庆没建网站的企业
  • 做购物网站怎么写开题报告专业团队值得信赖
  • 招聘织梦网站网站开发原型模板
  • 先注册域名后建设网站可以吗kxsw wordpress
  • 网站数据不变重新安装wordpressseo的目的是什么
  • 临沂专业网站建设公司哪家好网站建设与管理怎么做
  • 自助建站系统搭建网站域名是什么?
  • 深圳高端网站定制公司工作微信管理系统
  • wordpress表单提交邮件通知厦门seo结算
  • 沈阳网站建设首选龙兴科技网站开发和ui的区别
  • 手机网站底部电话柳市做网站的公司
  • 荥阳市城乡建设规划网站网络营销外包顾问
  • 免费网站开发住建部城乡建设网站
  • 湖北建设厅行政服务中心网站苏州高端网站建设机构
  • 建筑模板种类连云港seo公司