X

曜彤.手记

随记,关于互联网技术、产品与创业

C++17 部分新特性一览

C++17 又称 C++1z,是 C++ 的现行标准的非正式名称,正式名称为 “International Standard ISO/IEC Programming Language C++”。C++17 旨在作为大型扩展,最终的投票期于2017年结束。

  1. 静态断言 static_assert 无需提供出错信息:
/**
 * static_assert(bool_constexpr, message) (since C++11)
 * static_assert(bool_constexpr)          (since C++17)
 */
int main(int argc, char **argv) {
  static_assert(1 == 1);  // 不需提供第二个参数;
  return 0;
}
  1. 新增 std::basic_string_view, 对外部字符串或字符串片断(string-slice)的引用。该方式只读、不会创建临时对象,string_view 仅使用一个指针和长度值来表示一个字符串对应的视图,在从 char* 进行初始化时的时间成本为 O(n),但接下来的 substr 操作其成本便为 O(1)。在使用时要注意:其引用的字符串生命周期必须要大于等于 string_view 本身。
/**
 * constexpr basic_string_view() noexcept;                                         (since C++17)
 * constexpr basic_string_view(const basic_string_view& other) noexcept = default; (since C++17)
 * constexpr basic_string_view(const CharT* s, size_type count);                   (since C++17)
 * constexpr basic_string_view(const CharT* s);                                    (since C++17)
 */
#include<string_view>
#include<iostream>
int main(int argc, char **argv) {
  const char* str = "Hello, world!";
  std::string_view sv(str, std::strlen(str));
  std::cout << sv.substr(7) << std::endl;
  return 0;
}
  1. 移除 trigraphs 三元符(诸如 “??=” 等)。
  2. 允许在模板的模板(template template)参数中使用 typename(之前仅允许使用 class):
template<typename T>
struct B { T t(); };
template<template<typename> typename V, typename T>
struct A {
  A(V<T>&& v) {}
};
int main(int argc, char **argv) {
  A a = B<int>();
  return 0;
}
  1. 编译期静态确定的 if 判断式:if constexpr (expression)。可优化模板泛型编程,比如减少需要自定义的边界模板(循环体+边界条件)。编译时需指定 “-O1” 以上的优化等级,默认情况下编译时求值不会进行:
template <int N, int ...Ns>
auto sum() {
  if constexpr (sizeof...(Ns) == 0) {
    return N;
  } else {
    return N + sum<Ns...>();
  }
}
int main(int argc, char **argv) {
  std::cout << sum<1, 2, 3>() << std::endl;
  return 0;
}
  1. 变长参数模板的 Folding(折叠)运算,可以用来简化模板展开:
template <typename ...Args>
auto sum(Args ...args) { return (0 + ... + args); }  // 二元折叠;
int main(int argc, char **argv) {
  std::cout << sum(1, 1.3, 1) << std::endl;
  return 0;
}
  1. if 与 switch 语句内的初始化:
int main(int argc, char **argv) {
  if (int x = 0; x == 0) {
    std::cout << x << std::endl;
  }
  switch(int y = 0; y) {
    case 0: {
      std::cout << y << std::endl;
    }
  }
  return 0;
}
  1. std::variant,可作为类型安全(type-safe)的联合体所使用的容器(union container),同时借助 std::visit 可以代替旧联合体类型使用的 Tagged-Union 方式:

struct A {
  int v;
  void echo() const {
    std::cout << v << std::endl;
  }
  A(int v) : v(v) {}
  A(const A& rhs) { v = rhs.v; }
};
int main(int argc, char **argv) {
  std::variant<int, A> v = {A(100)};
  std::visit([](auto&& arg) {  // 使用 std::visit 在编译时遍历 std::variabt 的内容;
    using T = std::decay_t<decltype(arg)>;
    if constexpr (std::is_same_v<T, A>) { arg.echo(); }
  }, v);
  return 0;
}
  1. 结构化绑定,允许对函数返回的结构体对象进行解构
struct A {
  int x;
  std::string s;
  A() : x(10), s("YHSPY") {}
};
int main(int argc, char **argv) {
  const auto [x, s] = A();
  std::cout << x << s << std::endl;
  return 0;
}
  1. 利用类模板的构造函数来推导模板参数类型:
template<typename T>
struct A {
  A(T t) {}
};
int main(int argc, char **argv) {
  A(10);  // 不需要 A<int>(10);
  return 0;
}

(随时补充)




评论 | Comments


Loading ...