删除了 C++17 的特性
C++17 使编写简单、清晰和更具表现力的代码成为可能。 C++17 中引入的一些特性是:
- 嵌套命名空间
- if 和 switch 中的变量声明
- if constexpr 语句
- 结构化绑定
- 折叠表达式
- 枚举的直接列表初始化
在新版本的 C++17 中,引入了许多新功能,但删除或弃用了一些功能。下面列出了这些:
- 删除不推荐使用的运算符++
- 移除寄存器
- 移除 auto_ptr
- 三合字母
- 抛出(typeid)
- std::函数的分配器支持
- std::pointer_to_unary_function 和 std::pointer_to_binary_function
- std::binder1st 和 std::binder2nd
- std::bind1st 和 std::bind2nd
- 其他功能。
让我们开始详细讨论这些功能。
1. 删除不推荐使用的运算符++:后缀和前缀增量 (++) 表达式现在对 bool 操作数无效,因为前缀和后缀运算符 ++ 对于类型 bool 已重载,但在这两种情况下,都是 bool 参数的返回值是真的。 bool 类型不支持完整的算术类型集。自 C++98 推出以来,人们一直在等待这种变化。在 C++17 的新版本中,它不再被视为算术类型,并且这些运算符已被弃用。
替代方案: std::exchange可用作替代方案,但仅限于后缀运算符具有有效用途的情况。交换函数用新值替换对象的值并返回对象的旧值。
2. 移除寄存器:很久以前,在 C++11 中不推荐使用 register 关键字。 register 关键字指定或提示编译器可以将变量放在寄存器中以便快速访问,或者这些变量可能会被大量使用,以便它可以通过将它们存储在 CPU 寄存器中来进行优化。但是编译器会进行隐式优化,并且很少使用提示。因此,在新版本中, register 关键字被删除了,尽管该关键字仍保留供以后的版本使用。
句法:
register string s = "Register on GfG"
替代方案: register 没有替代方案,因为编译器会自动执行相同的工作。
3. 移除auto_ptr: auto_ptr 被用来创建一个智能指针来处理对象的生命周期。它是它所引用的对象的所有者。当一个对象被销毁时, auto_ptr也会自动销毁。这个智能指针在其复制构造函数中悄悄地窃取了托管对象的所有权,并从右侧参数中进行了复制赋值。结果,副本与原始智能指针对象不同。由于这些复制语义, auto_ptr不能作为CopyConstructible工作,因此它已被弃用。
替代方案: auto_ptr可以很容易地替换为, unique_ptr 这也是一个具有类似工作但具有更高安全性的智能指针。它在 C++11 中被引入作为auto_ptr的直接替代品,因为它提供了新功能(删除)和对数组的支持。此外,它只允许引用指针的一个所有者。因此,在使用unique_ptr 时,一个资源最多只能有一个unique_ptr并且当它被销毁时,该资源会自动声明。如果尝试复制unique_ptr ,则会导致编译时错误。
例子:
unique_ptr p1 (new T);
unique_ptr p2 = p1;
// Error: can't copy unique_ptr
4. Trigraphs: Trigraphs 是一组三个字符。基本上,它是一个特殊字符序列,用作某些字符的替代。它由两个问号表示。
例子:
??- produces ~
??= produces #
??/ produces \
??’ produces ^
??( produces [
??) produces ]
??! produces |
??< produces {
??> produces }
但是它们会产生很多混乱,因为它们在评论之前被解析,因此在最新版本中被删除。
替代方案: C++17 没有为 Trigraph 提供任何替代方案,因为现代键盘具有所有这些功能。此外,它会在代码中产生很多错误。
5. throw(typeid):如果任何函数声明为在其异常规范中列出的类型 T,则该函数可能会向该类型或从该类型派生的类型抛出异常。这是不推荐使用的动态异常规范的非抛出版本,现在已删除。它已被替换为noexcept ,这具有更明确的含义。
句法:
throw(typeid, typeid, ...)
例子:
void throwsInt(int x) throw(int)
{
cout<<"throw function replaced with noexcept :)";
if (x == 0)
{
throw 1;
}
}
替代方案:如上所述, throw 可以有更好的替代方案noexcept 。它指定函数是否可以在不指定其类型的情况下抛出异常。但是只有在函数调用不能抛出任何错误时才使用它,否则程序将终止。
6. std:: 函数的分配器支持:几个构造函数允许指定用于分配内部内存的分配器。 std::函数也有采用分配器参数的构造函数,但语义不清楚,并且在将分配器存储在类型擦除的上下文中然后稍后为复制分配期间所需的任何分配恢复该分配器时存在技术故障。因此,在 C++17 中删除了这些构造函数重载。
替代方案: C++ 中没有这样的特性,它取代了分配器。
7. std::pointer_to_unary_function, std::pointer_to_binary_function: std::pointer_to_unary_function , std::pointer_to_binary_function函数对象充当一元或二元函数的包装器。这些函数包括一个构造函数,该构造函数使用提供的函数和运算符()函数构造一个新的pointer_to_unary_function对象,该函数调用存储的函数。
替代方案:这两个函数std:: 函数和std::ref替换std::pointer_to_unary_function 、 std::pointer_to_binary_function 。
8. std::binder1st 和 std::binder2nd:这些是将参数绑定到二元函数的函数对象。参数的值在构造时传递给对象并存储在对象中。每当通过运算符() 函数调用函数对象时,存储的值作为参数之一传递,另一个参数作为运算符() 的参数传递。生成的函数对象是一元函数。
- binder1st:将第一个参数绑定到构造对象时给定的值。
- binder2nd:将第二个参数绑定到构造对象时给定的值。
替代方案: Lambdas 、 std::bind是两个可以替代 binder1st 和 binder2nd 的特性。
9. std::bind1st 和 std::bind2nd:这些是创建 std::binder1st 或 std::binder2nd 实例的辅助函数,它们将给定参数绑定到给定二元函数对象的第一个或第二个参数。但是这些在引入 lambdas 时没有用 在 C++11 中,因此它们已被弃用。
10、其他功能:
- std::mem_fun_t,
- std::mem_fun1_t
- std::const_mem_fun_t
- std::const_mem_fun1_t
- std::mem_fun_ref_t
- std::mem_fun1_ref_t
- std::const_mem_fun_ref_t
- std::const_mem_fun1_ref_t
这些是函数对象,它们包装了一个指向没有参数或只有一个参数的成员函数的指针。要调用的成员函数作为指针传递给运算符() 的类实例,即,要调用的成员函数由指针传递给后者的调用运算符的对象,作为引用传递。它们已被弃用,因为它们仅限于没有参数或只有一个参数的成员函数,并且需要不同的函数和函数对象来处理对类实例的指针或引用。
替代方案:上述函数的替代方案是std::mem_fn ,它可以处理具有任意数量变量的成员函数,不仅可以处理对象的引用或指针,还可以处理智能指针。