文章

c++17新特性

移除

auto_ptr被移除

由于以下原因被移除

  • 语义不清晰。auto_ptr的拷贝被改写为移动,很容易让认误解。在拷贝时原指针被赋NULL,很容易导致重大错误。
  • 风格与现代c++不符,移动操作现在已经有移动语义了。现在右值转发都使用移动语义进行,显得auto_ptr的这种拷贝移动更奇怪了。

功能上,原来使用auto_ptr可以由unique_ptr替代。

random_shuffle被移除

random_shuffle打乱两个迭代器之间的数据。功能被更强大的shuffle替代,因此移除

unexpected被移除

若函数抛出了未列于其异常说明的类型的异常,则调用函数 unexpected。缺省的函数调用 terminate,但它可以(通过 set_unexpected)替换为用户提供的函数,可能调用 terminate 或抛出异常。若从 unexpected 抛出的异常为异常说明所接受,则栈回溯照常持续。若它不被接受,但异常说明允许 bad_exception,则抛出 bad_exception。否则,调用 terminate

这个和throw语法是一个体系的,随着异常说明整个体系的大概,unexpected也取消了。不知道为什么要改,也不知道没改之前怎么用的。现在高阶异常编程用的也少,等下次用到再说。

三标符被移除

这个是早期c++的奇怪特性,用来字符替换的。被用的极少,替换了就算了。

新增

折叠表达式

用来配合形参包语法的。看下面例子就懂了。

template<typename... Args>
bool all(Args... args) { return (... && args); }
 
bool b = all(true, true, true, false);
 // 在 all() 中,一元左折叠展开成
 //  return ((true && true) && true) && false;
 // b 为 false
void printer(Args&&... args) {
    (std::cout << ... << args) << '\n';
}
// 此函数将参数一个个打出

类模板实参推导

原来c++有个泛型函数自动推导类型的,现在模板类也可以了。

std::pair p(2, 4.5);     // 推导出 std::pair<int, double> p(2, 4.5);
std::tuple t(4, 3, 2.5); // 同 auto t = std::make_tuple(4, 3, 2.5);
std::less l;             // 同 std::less<void> l;

auto 占位的非类型模板形参

泛型方法可以配置若干的模板形参,里面有类型的和非类型的。例如template<T,int a>里面,就有这两种,非类型的一般用来完成函数式编程的有个什么xxx化(忘了,回头查查)。现在可以不写具体的类型,用autoauto&以及其他系列的占位了。

当然由于历史原因,c++20前不支持浮点型作为非类型形参。

if优化

主要优化了两个地方。

  • 条件可以加上constexpr,后接一个编译期的函数
template <typename T>
auto get_value(T t) {
    if constexpr (std::is_pointer_v<T>)
        return *t; // 对 T = int* 推导返回类型为 int
    else
        return t;  // 对 T = int 推导返回类型为 int
}
  • 条件前可以加一个初始化语句
if (auto it = m.find(10); it != m.end()) { return it->second.size(); }

inline优化

inline的定义从允许内联改为允许多次定义(编译时不会编译为不同地址)。因此inline可以适用于变量。一般用于在多个文件中引用的头文件中定义某一个变量或(全局变量),或者在头文件中进行某种类初始化(此时将类中需要如此操作的变量定义为inline)

结构化绑定

有点类似于python中的a,b = b,a的超级加强版。

float x{};
char  y{};
int   z{};
 
std::tuple<float&,char&&,int> tpl(x,std::move(y),z);
const auto& [a,b,c] = tpl;
// a 指名指代 x 的结构化绑定;decltype(a) 为 float&
// b 指名指代 y 的结构化绑定;decltype(b) 为 char&&
// c 指名指代 tpl 的第 3 元素的结构化绑定;decltype(c) 为 const int

以上是最简单的绑定到数组,复杂的用到再说。

支持嵌套命名空间

嵌套命名空间定义:namespace A::B::C { ... } 等价于 namespace A { namespace B { namespace C { ... } } }。这个还是挺有用的。不过命名空间一般没那么复杂。因为大家都喜欢用类包来包去。

using可以声明多个名称

字面意思

namespace X {
    using ::f;        // 全局 f 现在作为 ::X::f 可见
    using A::g;       // A::g 现在作为 ::X::g 可见
    using A::g, A::g; // (C++17) OK:命名空间作用域允许双重声明
}

noexcept作为函数声明的一部分

noexcept使用更加自由,可以在typedef等地方出现。

求值顺序修改

优化了”按顺序早于“里面的细枝末节的问题

lambda支持捕捉*this

字面意思

struct S2 { void f(int i); };
void S2::f(int i)
{
    [=]{};          // OK:默认以复制捕获
    [=, &i]{};      // OK:以复制捕获,但 i 以引用捕获
    [=, *this]{};   // C++17 前:错误:无效语法
                    // C++17 起:OK:以复制捕获外围的 S2
    [=, this] {};   // C++20 前:错误:= 为默认时的 this
                    // C++20 起:OK:同 [=]
}

属性命名空间不必重复

[[using CC: opt(1), debug]] // 同 [[CC::opt(1), CC::debug]]

虽然是字面意思,但是我从没用过这个属性语法,不知道是干嘛的。

__has_include语法

预编译器宏,功能就是字面意思。这个可以用来替代早期的程序在#include的时候,为表示已经声明过了此文件而define一个宏的写法。

库特性

不在此赘述,概括一下就是优化与新增STL。

License:  CC BY 4.0