C++ 17允许编写简单,更清晰,更具表现力的代码。 C++ 17中引入的一些功能包括:
- 嵌套命名空间
- if和switch中的变量声明
- 如果constexpr语句
- 结构化绑定
- 折叠式
- 枚举的直接列表初始化
嵌套命名空间
命名空间是一种非常方便的工具,可用于组织和构造代码库,将逻辑上属于同一组的组件(如类和函数)组合在一起。
让我们考虑一下视频游戏引擎的假设代码库。在此,为整个游戏引擎定义了一个名称空间,因此将在该公共名称空间下声明在该游戏引擎中实现的所有类和功能。为了进行更清晰的定义,您可以在全局名称空间下定义另一个名称空间,假设Graphics是一个子名称空间,现在将执行图形操作的所有类放在该名称空间下,依此类推。
- 在C++ 17之前:
以下是用于嵌套名称空间的语法:
// Below is the syntax for using // the nested namespace namespace Game { namespace Graphics { namespace Physics { class 2D { .......... }; } } }
- 使用C++ 17时:
在C++ 17之前,您必须使用这种冗长的语法在嵌套名称空间中声明类,但是C++ 17引入了一项新功能,该功能可以打开嵌套的名称空间,而无需使用这种忙碌的语法,而该语法要求重复的名称空间关键字并保持跟踪。打开和关闭大括号。在C++ 17中,使用双冒号引入嵌套的名称空间的方法简洁明了。语法如下:
// Below is the syntax to use the // nested namespace in one line namespace Game::Graphics::Physics { class 2D { .......... }; }
这使得代码不太容易出错,因为无需注意括号的几个级别。
if和switch中的变量声明
- 在C++ 17之前:
假设一个字符串向量,并且如果向量中存在字符串“ abc” ,则要将其替换为“ $$$” 。为此,您可以调用标准的find()函数以在向量中搜索项目,如下所示:
// Below is the approach for replace // any string with another string // in vector of string vector
str // Find and replace abc with $$$ const auto it = find(begin(str), end(str), "abc"); if (it != end(str)) { *it = "$$$"; } 解释:
- find算法将返回一个指向匹配字符串的迭代器。
- 现在,如果再次要用同一向量中的其他字符串替换另一个字符串,则为此,请按照上面显示的相同方法,因为您将重复相同的代码,只需要将迭代器的名称更改为其他的东西。
- 在同一作用域中声明两个以上具有相同名称的迭代器会产生编译错误。名称更改选项将起作用,但是如果有多个字符串需要替换,则此方法效率不高。
- 使用C++ 17时:
为了处理这种情况,C++ 17通过在if语句中声明变量来提供更好的选择,如下所示:// Below is the syntax for replacing a // string in vector of string in C++17 if (const auto it = find(begin(str), end(str), "abc"); it != end(str)) { *it = "$$$"; }
现在,迭代器“ it”的范围位于if语句本身之内,并且相同的迭代器名称也可以用于替换其他字符串。
使用switch语句使用下面给出的语法可以完成相同的操作:switch (initial-statement; variable) { .... // Cases }
以下是替换仅在C++ 17中运行的给定向量中的一些定义的字符串的程序:
C++
// C++ 17 code to demostrate if constexpr #include
#include #include #include using namespace std; // Helper function to print content // of string vector void print(const string& str, const vector & vec) { cout << str; for (const auto& i : vec) { cout << i << " "; } cout << endl; } // Driver Code int main() { // Declare vector of string vector vec{ "abc", "xyz", "def", "ghi" }; // Invoke print helper function print("Initial vector: ", vec); // abc -> $$$, and the scope of "it" // Function invoked for passing // iterators from begin to end if (const auto it = find(begin(vec), end(vec), "abc"); // Check if the iterator reaches // to the end or not it != end(vec)) { // Replace the string if an // iterator doesn't reach end *it = "$$$"; } // def -> ### // Replace another string using // the same iterator name if (const auto it = find(begin(vec), end(vec), "def"); it != end(vec)) { *it = "###"; } print("Final vector: ", vec); return 0; }
C++
// C++ 17 code to demostrate error // generated using if statement #include
#include #include using namespace std; // Template Class template auto length(T const& value) { // Check the condition with if // statement whether T is an // integer or not if (is_integral ::value) { return value; } else { return value.length(); } } // Driver Code int main() { int n{ 10 }; string s{ "abc" }; cout << "n = " << n << " and length = " << length(n) << endl; cout << "s = " << s << " and length = " << length(s) << endl; }
C++
// C++ 17 code to demostrate if constexpr #include
#include #include using namespace std; // Template Class template auto length(T const& value) { // Check the condition with if // statement whether T is an // integer or not if constexpr(is_integral ::value) { return value; } else { return value.length(); } } // Driver Code int main() { int n{ 10 }; string s{ "abc" }; cout << "n = " << n << " and length = " << length(n) << endl; cout << "s = " << s << " and length = " << length(s) << endl; }
C++
// C++ 17 program to demonstrate // Structure Bindings #include
#include
C++
// C++ program to illustrate the // folding expression in C++17 #include
#include using namespace std; // Template Class template auto sum(Args... args) { return (args + ... + 0); } template auto sum2(Args... args) { return (args + ...); } // Driver Code int main() { // Function Calls cout << sum(11, 22, 33, 44, 55) << "\n"; cout << sum2(11, 22, 33, 44, 55) << "\n"; return 0; }
C++
// Program to illustrate std::byte // in the C++ 17 #include
#include using namespace std; // Function to print byte a void Print(const byte& a) { cout << to_integer (a) << endl; } // Driver Code int main() { byte b{ 5 }; // Print byte Print(b); // A 2-bit left shift b <<= 2; // Print byte Print(b); // Initialize two new bytes using // binary literals byte b1{ 0b1100 }; byte b2{ 0b1010 }; Print(b1); Print(b2); // Bit-wise OR and AND operations byte byteOr = b1 | b2; byte byteAnd = b1 & b2; // Print byte Print(byteOr); Print(byteAnd); return 0; }
输出:
如果constexpr语句
编写模板代码时,C++ 17的此功能非常有用。正常的if语句条件是在运行时执行的,因此C++ 17引入了这个新的if constexpr语句。主要区别在于,如果constexpr是在编译时求值的。基本上,constexpr函数是在编译时评估的。那么,为什么如此重要,其主要重要性与模板代码有关。
- 在C++ 17之前:
假设要比较一个整数变量是否带有值,则仅在使用该变量之前在编译时声明并初始化该整数,如下所示:// Below is the syntax for using // If-else statement int x = 5; // Condition if (x == 5) { // Do something } else { // Do something else }
- 使用C++ 17时:
假设您有一些在某些通用类型T上运行的模板。
// Below is the generic code for // using If else statement template
// Function template for illustrating // if else statement auto func(T const &value) { if constexpr(T is integer) { // Do something } else { // Something else } } 因此constexpr的一个方面是,现在编译器知道T是否为整数,并且编译器仅考虑满足条件的子语句,因此仅编译该代码块,而C++编译器忽略其他子语句。
以下是相同的程序:
C++
// C++ 17 code to demostrate error // generated using if statement #include
#include #include using namespace std; // Template Class template auto length(T const& value) { // Check the condition with if // statement whether T is an // integer or not if (is_integral ::value) { return value; } else { return value.length(); } } // Driver Code int main() { int n{ 10 }; string s{ "abc" }; cout << "n = " << n << " and length = " << length(n) << endl; cout << "s = " << s << " and length = " << length(s) << endl; } 输出:
error: request for member ‘length’ in ‘value’, which is of non-class type ‘const int’
解释:
- 在上面的代码中,如果程序被编译,则将产生编译错误,因为integer没有名为length()的函数,并且正如我们仅在if语句中使用过的那样,整个代码将被编译并产生错误。
- 为了避免这种错误,即,仅考虑将重要的C++ 17代码用于救援。
- 因此在用if constexpr替换if的情况下,如果T是整数,则仅编译constexpr条件下的条件(因为它满足T为整数的条件),而不是包含length()函数的else部分(产生了错误)。
- 仅当T不是整数(例如,字符串,才考虑else块,因为它具有length()函数,所以不会产生错误,并且会打印字符串的长度。
下面是正确的代码:
C++
// C++ 17 code to demostrate if constexpr #include
#include #include using namespace std; // Template Class template auto length(T const& value) { // Check the condition with if // statement whether T is an // integer or not if constexpr(is_integral ::value) { return value; } else { return value.length(); } } // Driver Code int main() { int n{ 10 }; string s{ "abc" }; cout << "n = " << n << " and length = " << length(n) << endl; cout << "s = " << s << " and length = " << length(s) << endl; } 输出:
结构绑定
从根本上说,它允许您声明多个变量,这些变量使用对,通用元组或自定义结构中的值进行初始化,并且这些多变量声明在单个语句中发生。
- 在C++ 17之前:
在C++ 17之前, std :: tie用于声明多个用自定义结构中的值初始化的变量。
// Using tuple int a, b, c; std::tie(a, b, c) = std::make_tuple(1, 2, 3);
- 使用C++ 17时:
假设您有一个字典,其名称为键,而其最喜欢的语言为值,并且这是使用标准容器映射实现的,并且您想使用insert方法向其中插入新条目。此insert方法返回一个std :: pair,其中包含两条信息,该对中的第一项是迭代器,第二项是布尔值。
// Below is the code to use // structure binding in C++17 map
fav_lang{ { "John", "Java" }, { "Alex", "C++" }, { "Peter", "Python" } }; auto result = fav_lang.insert({ "Henry", "Golang" }); 这里有两种情况需要考虑:
- 词典中不存在新的还是已经存在的。如果字典中不存在新的关联(键值对),则会将其插入。因此,在这种情况下,返回的对包含指向新元素的迭代器,并且布尔值变为True。
- 如果新密钥已经存在,则迭代器指向现有密钥,并且布尔值变为False。
现在写代码的布尔标志和插入迭代器,第一写入检查。首先和。第二,以一对存取元件。 C++ 17在以下方面可以做得更好:
- 使用C++ 17结构绑定来声明和初始化两个变量,它们的名称比第一个和第二个更有意义。
- 使用名称位置和成功比使用第一和第二要清楚得多。
- 位置和成功的含义非常简单,即位置告诉迭代器在哪里,成功告诉元素是否插入。
以下是相同的程序:
C++
// C++ 17 program to demonstrate // Structure Bindings #include
#include 输出:
折叠式
C++ 11提供了可变参数模板的选项,可与可变数量的输入参数一起使用。折叠表达式是使用运算符解压缩可变参数的一种新方法。语法如下:
(pack op …)
(… op pack)
(pack op … op init)
(init op … op pack)
where pack represents an unexpanded parameter pack, op represents an operator and init represents a value.
- (pack op…):这是一个正确的折叠,它像pack1 op(…op(packN-1 op packN))一样展开。
- (…op pack):这是向左折叠的,类似于((pack1 op pack2)op…)op packN一样。
- (pack op…op init):这是一个二进制的右折,它像pack1 op(… op(packN-1 op(packN op init))一样扩展了)。
- (init op…op pack):这是一个二进制左折,类似于((((init op pack1)op pack2)op…)op packN一样展开。
- 在C++ 17之前:
创建一个函数,该函数接受可变数量的参数并返回参数的总和。// Below is the function that implements // folding expressions using variable // number of arguments int sum(int num, ...) { va_list valist; int s = 0, i; va_start(valist, num); for (i = 0; i < num; i++) s += va_arg(valist, int); va_end(valist); return s; }
- 使用C++ 17时:
要通过可变参数模板实现诸如sum等的递归函数,在C++ 17中这将变得比C++ 11实现更好。下面是相同的模板类:
// Template for performing the // recursion using variadic template auto C11_sum() { return 0; } // Template Class template
auto C11_sum(T1 s, T... ts) { return s + C11_sum(ts...); } 下面是说明相同内容的程序:
C++
// C++ program to illustrate the // folding expression in C++17 #include
#include using namespace std; // Template Class template auto sum(Args... args) { return (args + ... + 0); } template auto sum2(Args... args) { return (args + ...); } // Driver Code int main() { // Function Calls cout << sum(11, 22, 33, 44, 55) << "\n"; cout << sum2(11, 22, 33, 44, 55) << "\n"; return 0; } 输出:
枚举的直接列表初始化
在C++ 17中,允许使用花括号对枚举进行初始化。以下是相同的语法:
enum byte : unsigned char {};
byte b {0}; // OK
byte c {-1}; // ERROR
byte d = byte{1}; // OK
byte e = byte{256}; // ERROR
C++ 17的某些库功能:
- std :: byte {b}:这是一种独特的类型,它应用了C++语言定义中指定的字节概念。字节是位的集合,在这种情况下只能使用按位运算运算符。下面是说明相同内容的程序:
C++
// Program to illustrate std::byte // in the C++ 17 #include
#include using namespace std; // Function to print byte a void Print(const byte& a) { cout << to_integer (a) << endl; } // Driver Code int main() { byte b{ 5 }; // Print byte Print(b); // A 2-bit left shift b <<= 2; // Print byte Print(b); // Initialize two new bytes using // binary literals byte b1{ 0b1100 }; byte b2{ 0b1010 }; Print(b1); Print(b2); // Bit-wise OR and AND operations byte byteOr = b1 | b2; byte byteAnd = b1 & b2; // Print byte Print(byteOr); Print(byteAnd); return 0; } 输出:
- std :: filesystem():它提供了一种处理目录和文件的标准方法。在下面的示例中,如果有可用空间,则将文件复制到临时路径。以下是相同的模板:
// For manupulating the file // directories const auto FilePath {"FileToCopy"}; // If any filepath exists if(filesystem::exists(FilePath)) { const auto FileSize { filesystem::file_size(FilePath) }; filesystem::path tmpPath {"/tmp"}; // If filepath is available or not if(filesystem::space(tmpPath) .available > FileSize) { // Create Directory filesystem::create_directory( tmpPath.append("example")); // Copy File to file path filesystem::copy_file(FilePath, tmpPath.append("newFile")); } }
- std :: apply():其参数是将被调用的可调用对象,以及需要将其元素用作参数的元组。以下是相同的模板:
// Function that adds two numbers auto add = [](int a, int b) { return a + b; }; apply(add, std::make_tuple(11, 22));
- std :: any():类any为任何类型的单个值描述了类型安全的容器.any强制转换函数对提供的对象的访问是非成员的。