C++17新属性详解

C++迭代速度相对来说还是比较慢的,2010年以后,C++的新版本迭代速度有所加快,这一点,从C++标准版本的历史发布图1就可以看出来:

图1 C++正式版本发布历史

C++11算是更新比较大的一次了,引入了很多新属性,以至于C++11出来以后,好多C++同行感叹,这看了C++11感觉像是在学习一门新语言!哈哈,这可能跟C++属性众多和库众多有关吧。现在我们来看看C++17的新增属性吧。

C++17的入选特性有:

(1).非类型模板参数的 auto

模板参数分为两种,一种是类型模板参数,也是我们用得最多的一种:

  1. template <typename T, typename U>
  2. auto add(T t, U u) {
  3. return t+u;
  4. }

     里面的 T 和 U 都是类型模板参数。另一种是非类型模板参数,它可以让不同的字面量成为模板的参数:

  1. template <typename T, int BufSize>
  2. class buffer_t {
  3. public:
  4. T& alloc();
  5. void free(T& item);
  6. private:
  7. T data[BufSize];
  8. }

  9. buffer_t<int, 100> buf; // 100 作为模板参数

       遗憾的是我们在编写模板的时候就必须明确非类型模板参数的具体类型,C++17 打破了这一限制,让我们能够在非类型模板参数中使用 auto 关键字,从而让编译器推导具体的类型:

  1. template <auto value> void foo() {
  2. return;
  3. }

  4. foo<10>(); // value 被推导为 int 类型

(2).std::variant<>

       熟悉 boost 的人应该很早就听说过 variant<> 了。variant<> 可以用于存储和操作不同类型的对象。我们在前面(对标准库的扩充:新增容器)对于迭代 std::tuple 时,简单使用了 boost::variant<>。提供给 variant<> 的类型模板参数可以让一个 variant<> 从而容纳提供的几种类型的变量(在其他语言(例如 Python/JavaScript 等)表现为动态类型)。

       C++17 正式将 variant<> 纳入标准库,摇身一变成为 std::variant<>,有了它之后,我们可以将前面的代码更改为:

  1. #include <variant>
  2. template <size_t n, typename... Args>
  3. std::variant<Args...> _tuple_index(size_t i, const std::tuple<Args...>& tpl) {
  4. if (i == n)
  5. return std::get<n>(tpl);
  6. else if (n == sizeof...(Args) - 1)
  7. throw std::out_of_range("越界.");
  8. else
  9. return _tuple_index<(n < sizeof...(Args)-1 ? n+1 : 0)>(i, tpl);
  10. }
  11. template <typename... Args>
  12. std::variant<Args...> tuple_index(size_t i, const std::tuple<Args...>& tpl) {
  13. return _tuple_index<0>(i, tpl);
  14. }

(3).结构化绑定(Structured bindings)

       结构化绑定提供了类似其他语言中提供的多返回值的功能。到目前为止,我们可以通过 std::tuple 来构造一个元组,囊括多个返回值。但缺陷是显而易见的,我们没有一种简单的方法直接从元组中拿到并定义元组中的元素,尽管我们可以使用 std::tie 对元组进行拆包,但我们依然必须非常清楚这个元组包含多少个对象,各个对象是什么类型。

      C++17 给出的结构化绑定可以让我们写出这样的代码:

  1. std::tuple<int,double,std::string> f() {
  2. return std::make_tuple(1,2.3,"456");
  3. }
  4. auto [x,y,z] = f(); // x,y,z 分别被推导为int,double,std::string

(4).变量声明的强化

      变量的声明在虽然能够位于任何位置,甚至于 for 语句内能够声明一个临时变量 int,但始终没有办法在 if 和 switch语句中声明一个临时的变量。例如:

  1. auto p = map_container.try_emplace(key, value);
  2. if(!p.second) {
  3. //...
  4. } else {
  5. //...
  6. }

C++17 消除了这一限制,使得我们可以:

  1. if (auto p = m.try_emplace(key, value); !p.second) {
  2. //...
  3. } else {
  4. //...
  5. }


C++17未入选特性有:

(1).Concepts

      C++ 组委会在讨论投票最终确定 C++17 有很多提案,诸如 Concepts/Ranges/Module 等等,其中最受关注的就是 Concepts,可惜这一提案最终被拒,作为技术规范(Technical Specifications, TS) 将其发布。

      Concepts 是对 C++ 模板编程的进一步增强扩展。简单来说,Concepts 是一种编译期的特性,它能够让编译器在编译期时对模板参数进行判断,从而大幅度增强我们在 C++ 中模板编程的体验。使用模板进行编程时候我们经常会遇到各种令人发指的错误,这是因为到目前为止我们始终不能够对模板参数进行检查与限制,例如下面简单的两行代码会造成大量的几乎不可读的编译错误:

  1. #include <list>
  2. #include <algorithm>
  3. int main() {
  4. std::list<int> l = {1, 2, 3};
  5. std::sort(l.begin(), l.end());
  6. return 0;
  7. }

      而这段代码出现错误的根本原因在于,std::sort 对排序容器必须提供随机迭代器,否则就不能使用,而我们知道 std::list 是不支持随机访问的。用 Concepts 的话来说就是:std::list中的迭代器不满足std::sort中随机迭代器这个 Concepts(概念) 的 requirements(要求)。有了 Concepts,我们就可以这样:

  1. template <typename T>
  2. requires Sortable<T> // Sortable 是一个 concept
  3. void sort(T& c);

   缩写为:

  1. template<Sortable T> // T 是一个 Sortable 的类型名
  2. void sort(T& c)

甚至于直接将其作为类型来使用:

void sort(Sortable& c); // c 是一个 Sortable 类型的对象

遗憾的是,C++组委会没有将 Concetps 纳入新标准,而是将其作为TS正式发布(其实早在 C++11 最终定案之前就已经有 Concepts 的呼声了,但 Concepts TS 是2015年才完整正式发布),也就是我们现在看到的 Concepts TS。C++组委会拒绝将 Concepts 纳入新标准的原因其实很简单,并不是技术层面上的原因,纯粹是觉得它还不够成熟。

Concepts TS 的发布到最后一次 C++17 的讨论会只相隔了不到四个月的时间,Concepts 的(唯一)实现只存在于一个未发布的 gcc 版本中。而 gcc 中关于 Concepts 的实现就是由撰写 Concepts TS 的人开发的,虽然它能够进行相关测试,但还没有认真讨论过这份 TS 会产生哪些不良后果,更何况这份 TS 都没有被测试过。此外,已知的 Concepts 的一个明显的作用就是去辅助实现 Ranges TS 等提案,但实际上它们也没有被选入 C++17,所以可以把 Concepts 继续延后。

总的来说,类似于 Concepts/Ranges/Modules 这些令人兴奋的特性并没有入选至 C++17,这注定了 C++17 某种意义上来说相较于 C++11/14 依然只是小幅度更新,但我们有望在 C++2x 中看到这些东西的出现。

C++11/14/17对C++编译器的支持情况:

http://en.cppreference.com/w/cpp/compiler_support

(0)

相关推荐