翻译自https://github.com/AnthonyCalandra/modern-cpp-features

C++20语言特性

Concepts

Concepts are named compile-time predicates which constrain types. They take the following form:

Concepts被称为编译时断言,用于约束类型。有如下格式:

template < template-parameter-list >
concept concept-name = constraint-expression;

where constraint-expression evaluates to a constexpr Boolean. Constraints should model semantic requirements, such as whether a type is a numeric or hashable. A compiler error results if a given type does not satisfy the concept it’s bound by (i.e. constraint-expression returns false). Because constraints are evaluated at compile-time, they can provide more meaningful error messages and runtime safety.

constraint-expression 能编译时计算为常量表达式constexpr Boolean。约束是根据语义建模,比如类型是否是数字型或是否可以hash。如果实际类型不匹配(constraint-expression 返回的是false),编译期就会收到一个错误。因为约束是编译时计算的,约束能提供有意义的错误信息和运行时安全。

// `T` is not limited by any constraints.
template <typename T>
concept always_satisfied = true;
// Limit `T` to integrals.
template <typename T>
concept integral = std::is_integral_v<T>;
// Limit `T` to both the `integral` constraint and signedness.
template <typename T>
concept signed_integral = integral<T> && std::is_signed_v<T>;
// Limit `T` to both the `integral` constraint and the negation of the `signed_integral` constraint.
template <typename T>
concept unsigned_integral = integral<T> && !signed_integral<T>;

There are a variety of syntactic forms for enforcing concepts:

下面列举各种的强制概念的语法形式:

// Forms for function parameters:
// `T` is a constrained type template parameter.
template <my_concept T>
void f(T v);

// `T` is a constrained type template parameter.
template <typename T>
  requires my_concept<T>
void f(T v);

// `T` is a constrained type template parameter.
template <typename T>
void f(T v) requires my_concept<T>;

// `v` is a constrained deduced parameter.
void f(my_concept auto v);

// `v` is a constrained non-type template parameter.
template <my_concept auto v>
void g();

// Forms for auto-deduced variables:
// `foo` is a constrained auto-deduced value.
my_concept auto foo = ...;

// Forms for lambdas:
// `T` is a constrained type template parameter.
auto f = []<my_concept T> (T v) {
  // ...
};
// `T` is a constrained type template parameter.
auto f = []<typename T> requires my_concept<T> (T v) {
  // ...
};
// `T` is a constrained type template parameter.
auto f = []<typename T> (T v) requires my_concept<T> {
  // ...
};
// `v` is a constrained deduced parameter.
auto f = [](my_concept auto v) {
  // ...
};
// `v` is a constrained non-type template parameter.
auto g = []<my_concept auto v> () {
  // ...
};

The requires keyword is used either to start a requires clause or a requires expression:

requires关键字用于启动requires子句或requires表达式:

template <typename T>
  requires my_concept<T> // `requires` clause.
void f(T);

template <typename T>
concept callable = requires (T f) { f(); }; // `requires` expression.

template <typename T>
  requires requires (T x) { x + x; } // `requires` clause and expression on same line.
T add(T a, T b) {
  return a + b;
}

Note that the parameter list in a requires expression is optional. Each requirement in a requires expression are one of the following:

请注意,requires表达式中的参数列表是可选的。requires表达式中的每个需求都是以下内容之一:

Simple requirements - asserts that the given expression is valid.

简单需求-断言给出表达式是有效的.

template <typename T>
concept callable = requires (T f) { f(); };

Type requirements - denoted by the typename keyword followed by a type name, asserts that the given type name is valid.

类型要求-由typename关键字后跟类型名表示,断言给定的类型名是有效的。

struct foo {
  int foo;
};

struct bar {
  using value = int;
  value data;
};

struct baz {
  using value = int;
  value data;
};

// Using SFINAE, enable if `T` is a `baz`.
template <typename T, typename = std::enable_if_t<std::is_same_v<T, baz>>>
struct S {};

template <typename T>
using Ref = T&;

template <typename T>
concept C = requires {
                     // Requirements on type `T`:
  typename T::value; // A) has an inner member named `value`
  typename S<T>;     // B) must have a valid class template specialization for `S`
  typename Ref<T>;   // C) must be a valid alias template substitution
};

template <C T>
void g(T a);

g(foo{}); // ERROR: Fails requirement A.
g(bar{}); // ERROR: Fails requirement B.
g(baz{}); // PASS.

Compound requirements - an expression in braces followed by a trailing return type or type constraint.

复合要求-大括号中的表达式,后跟返回类型或类型约束。

template <typename T>
concept C = requires(T x) {
  {*x} -> typename T::inner; // the type of the expression `*x` is convertible to `T::inner`
  {x + 1} -> std::same_as<int>; // the expression `x + 1` satisfies `std::same_as<decltype((x + 1))>`
  {x * 1} -> T; // the type of the expression `x * 1` is convertible to `T`
};

Nested requirements - denoted by the requires keyword, specify additional constraints (such as those on local parameter arguments).

嵌套的需求-由requires关键字表示,指定附加约束(例如对局部参数参数的约束)。

template <typename T>
concept C = requires(T x) {
  requires std::same_as<sizeof(x), size_t>;
};

See also: concepts library.

Designated initializers指定初始值设定项

C-style designated initializer syntax. Any member fields that are not explicitly listed in the designated initializer list are default-initialized.

c风格的指定初始值设定项语法。任何没有显式的列在指定初始值设定项的成员变量使用默认值初始化。

struct A {
  int x;
  int y;
  int z = 123;
};

A a {.x = 1, .z = 2}; // a.x == 1, a.y == 0, a.z == 2

Template syntax for lambdas 模板式的lambdas

Use familiar template syntax in lambda expressions.

在lambda表达式中使用类似模板的语法。

auto f = []<typename T>(std::vector<T> v) {
  // ...
};

Range-based for loop with initializer基于循环体内的初始列表

This feature simplifies common code patterns, helps keep scopes tight, and offers an elegant solution to a common lifetime problem.

这个特性简化常见的代码模式,能帮助到代码更紧凑,提供了一个能更好解决常见生命周期的更优雅方案。

for (std::vector v{1, 2, 3}; auto& e : v) {
  std::cout << e;
}
// prints "123"

likely and unlikely attributes

Provides a hint to the optimizer that the labelled statement is likely/unlikely to have its body executed.

向优化器提供一个提示,即带标签的语句可能/不太可能执行其主体。

int random = get_random_number_between_x_and_y(0, 3);
[[likely]] if (random > 0) {
  // body of if statement
  // ...
}

[[unlikely]] while (unlikely_truthy_condition) {
  // body of while statement
  // ...
}

Deprecate implicit capture of this 已弃用隐式捕获this

Implicitly capturing this in a lamdba capture using [=] is now deprecated; prefer capturing explicitly using [=, this] or [=, *this].

在lambda表达式中隐式捕获this是已弃用的,使用[=, this] or [=, *this]来显式捕获this是更好的选择。

struct int_value {
  int n = 0;
  auto getter_fn() {
    // BAD:
    // return [=]() { return n; };

    // GOOD:
    return [=, *this]() { return n; };
  }
};

Class types in non-type template parameters

Classes can now be used in non-type template parameters. Objects passed in as template arguments have the type const T, where T is the type of the object, and has static storage duration.

类现在可以在非类型模板参数中使用。作为模板参数传入的对象具有类型“const T”,其中“T”是对象的类型,并且具有静态存储持续时间。

struct foo {
  foo() = default;
  constexpr foo(int) {}
};

template <foo f>
auto get_foo() {
  return f;
}

get_foo(); // uses implicit constructor
get_foo<foo{123}>();

constexpr virtual functions

Virtual functions can now be constexpr and evaluated at compile-time. constexpr virtual functions can override non-constexpr virtual functions and vice-versa.

虚函数可以使用constexpr,并且编译时计算。constexpr虚函数可以重载非constexpr虚函数,反之亦然。

struct X1 {
  virtual int f() const = 0;
};

struct X2: public X1 {
  constexpr virtual int f() const { return 2; }
};

struct X3: public X2 {
  virtual int f() const { return 3; }
};

struct X4: public X3 {
  constexpr virtual int f() const { return 4; }
};

constexpr X4 x4;
x4.f(); // == 4

explicit(bool)

Conditionally select at compile-time whether a constructor is made explicit or not. explicit(true) is the same as specifying explicit.

在编译时根据条件决定构造函数是否显式的,explicit(true)和直接使用explicit一样。

struct foo {
  // Specify non-integral types (strings, floats, etc.) require explicit construction.
  template <typename T>
  explicit(!std::is_integral_v<T>) foo(T) {}
};

foo a = 123; // OK
foo b = "123"; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)
foo c {"123"}; // OK

Immediate functions

Similar to constexpr functions, but functions with a consteval specifier must produce a constant. These are called immediate functions.

和constexpr函数相似,但是consteval函数必须产生一个常量,叫做immediate函数。

consteval int sqr(int n) {
  return n * n;
}

constexpr int r = sqr(100); // OK
int x = 100;
int r2 = sqr(x); // ERROR: the value of 'x' is not usable in a constant expression
                 // OK if `sqr` were a `constexpr` function

using enum

Bring an enum’s members into scope to improve readability. Before:

enum class rgba_color_channel { red, green, blue, alpha };

std::string_view to_string(rgba_color_channel channel) {
  switch (channel) {
    case rgba_color_channel::red:   return "red";
    case rgba_color_channel::green: return "green";
    case rgba_color_channel::blue:  return "blue";
    case rgba_color_channel::alpha: return "alpha";
  }
}

After:

enum class rgba_color_channel { red, green, blue, alpha };

std::string_view to_string(rgba_color_channel my_channel) {
  switch (my_channel) {
    using enum rgba_color_channel;
    case red:   return "red";
    case green: return "green";
    case blue:  return "blue";
    case alpha: return "alpha";
  }
}

Lambda capture of parameter pack

Capture parameter packs by value:

template <typename... Args>
auto f(Args&&... args){
    // BY VALUE:
    return [...args = std::forward<Args>(args)] {
        // ...
    };
}

Capture parameter packs by reference:

template <typename... Args>
auto f(Args&&... args){
    // BY REFERENCE:
    return [&...args = std::forward<Args>(args)] {
        // ...
    };
}

C++20 Library Features

Concepts library

Concepts are also provided by the standard library for building more complicated concepts. Some of these include:

标准库也提供了concepts相关库/concepts,用来构建更复杂的concepts,包括下面:

Core language concepts:

核心语言concepts:

  • same_as - specifies two types are the same.
  • derived_from - specifies that a type is derived from another type.
  • convertible_to - specifies that a type is implicitly convertible to another type.
  • common_with - specifies that two types share a common type.
  • integral - specifies that a type is an integral type.
  • default_constructible - specifies that an object of a type can be default-constructed.

Comparison concepts:

对比concepts:

  • boolean - specifies that a type can be used in Boolean contexts.
  • equality_comparable - specifies that operator== is an equivalence relation.

Object concepts:

  • movable - specifies that an object of a type can be moved and swapped.
  • copyable - specifies that an object of a type can be copied, moved, and swapped.
  • semiregular - specifies that an object of a type can be copied, moved, swapped, and default constructed.
  • regular - specifies that a type is regular, that is, it is both semiregular and equality_comparable.

Callable concepts:

  • invocable - specifies that a callable type can be invoked with a given set of argument types.
  • predicate - specifies that a callable type is a Boolean predicate.

See also: concepts.

Synchronized buffered outputstream

Buffers output operations for the wrapped output stream ensuring synchronization (i.e. no interleaving of output).

std::osyncstream{std::cout} << "The value of x is:" << x << std::endl;

std::span

A span is a view (i.e. non-owning) of a container providing bounds-checked access to a contiguous group of elements. Since views do not own their elements they are cheap to construct and copy – a simplified way to think about views is they are holding references to their data. Spans can be dynamically-sized or fixed-sized.

span是一个容器的视图(即不拥有),它提供对一组连续元素的边界检查访问。由于视图不拥有自己的元素,所以构造和复制它们的成本很低——考虑视图的一种简化方法是,它们保存了对其数据的引用。跨度可以动态调整大小或固定大小。

void f(std::span<int> ints) {
    std::for_each(ints.begin(), ints.end(), [](auto i) {
        // ...
    });
}

std::vector<int> v = {1, 2, 3};
f(v);
std::array<int, 3> a = {1, 2, 3};
f(a);
// etc.

Example: as opposed to maintaining a pointer and length field, a span wraps both of those up in a single container.

constexpr size_t LENGTH_ELEMENTS = 3;
int* arr = new int[LENGTH_ELEMENTS]; // arr = {0, 0, 0}

// Fixed-sized span which provides a view of `arr`.
std::span<int, LENGTH_ELEMENTS> span = arr;
span[1] = 1; // arr = {0, 1, 0}

// Dynamic-sized span which provides a view of `arr`.
std::span<int> d_span = arr;
span[0] = 1; // arr = {1, 1, 0}
constexpr size_t LENGTH_ELEMENTS = 3;
int* arr = new int[LENGTH_ELEMENTS];

std::span<int, LENGTH_ELEMENTS> span = arr; // OK
std::span<double, LENGTH_ELEMENTS> span2 = arr; // ERROR
std::span<int, 1> span3 = arr; // ERROR

Bit operations

C++20 provides a new <bit> header which provides some bit operations including popcount.

popcount返回二进制参数有几个是1的位。

std::popcount(0u); // 0
std::popcount(1u); // 1
std::popcount(0b1111'0000u); // 4

Math constants

Mathematical constants including PI, Euler’s number, etc. defined in the <numbers> header.

std::numbers::pi; // 3.14159...
std::numbers::e; // 2.71828...

std::is_constant_evaluated

Predicate function which is truthy when it is called in a compile-time context.

constexpr bool is_compile_time() {
    return std::is_constant_evaluated();
}

constexpr bool a = is_compile_time(); // true
bool b = is_compile_time(); // false

std::make_shared supports arrays

auto p = std::make_shared<int[]>(5); // pointer to `int[5]`
// OR
auto p = std::make_shared<int[5]>(); // pointer to `int[5]`

starts_with and ends_with on strings

Strings (and string views) now have the starts_with and ends_with member functions to check if a string starts or ends with the given string.

std::string str = "foobar";
str.starts_with("foo"); // true
str.ends_with("baz"); // false

Check if associative container has element

关联容器是否有值

Associative containers such as sets and maps have a contains member function, which can be used instead of the “find and check end of iterator” idiom.

map和set等关联容器可以使用contains成员函数取代以前find(),并检查返回的iterator是否是end。

std::map<int, char> map { {1, 'a'}, {2, 'b'} };
map.contains(2); // true
map.contains(123); // false

std::set<int> set {1, 2, 3};
set.contains(2); // true

std::bit_cast

A safer way to reinterpret an object from one type to another.

float f = 123.0;
int i = std::bit_cast<int>(f);

std::midpoint

Calculate the midpoint of two integers safely (without overflow).

std::midpoint(1, 3); // == 2

std::to_array

Converts the given array/”array-like” object to a std::array.

std::to_array("foo"); // returns `std::array<char, 4>`
std::to_array<int>({1, 2, 3}); // returns `std::array<int, 3>`

int a[] = {1, 2, 3};
std::to_array(a); // returns `std::array<int, 3>`

char8_t

Provides a standard type for representing UTF-8 strings.

char8_t utf8_str[] = u8"\u0123";

C++17 Language Features

Template argument deduction for class templates

Automatic template argument deduction much like how it’s done for functions, but now including class constructors.

模板类的构造函数自动模板参数推导,像过去的模板函数参数那样。

template <typename T = float>
struct MyContainer {
  T val;
  MyContainer() : val{} {}
  MyContainer(T val) : val{val} {}
  // ...
};
MyContainer c1 {1}; // OK MyContainer<int>
MyContainer c2; // OK MyContainer<float>

Declaring non-type template parameters with auto

用auto声明非模板参数

Following the deduction rules of auto, while respecting the non-type template parameter list of allowable types[*], template arguments can be deduced from the types of its arguments:

下面的auto的推导规则,在考虑允许类型[*]的非类型模板参数列表的同时,可以从其参数的类型中推导出模板参数:

template <auto... seq>
struct my_integer_sequence {
  // Implementation here ...
};

// Explicitly pass type `int` as template argument.
auto seq = std::integer_sequence<int, 0, 1, 2>();
// Type is deduced to be `int`.
auto seq2 = my_integer_sequence<0, 1, 2>();

* - For example, you cannot use a double as a template parameter type, which also makes this an invalid deduction using auto.

Folding expressions

折叠表达式

A fold expression performs a fold of a template parameter pack over a binary operator.

折叠表达式对二进制运算符执行模板参数包的折叠。

  • An expression of the form (... op e) or (e op ...), where op is a fold-operator and e is an unexpanded parameter pack, are called unary folds.
  • An expression of the form (e1 op ... op e2), where op are fold-operators, is called a binary fold. Either e1 or e2 is an unexpanded parameter pack, but not both.
template <typename... Args>
bool logicalAnd(Args... args) {
    // Binary folding.
    return (true && ... && args);
}
bool b = true;
bool& b2 = b;
logicalAnd(b, b2, true); // == true
template <typename... Args>
auto sum(Args... args) {
    // Unary folding.
    return (... + args);
}
sum(1.0, 2.0f, 3); // == 6.0

New rules for auto deduction from braced-init-list

auto在大括号中初始列表的推荐新规则

Changes to auto deduction when used with the uniform initialization syntax. Previously, auto x {3}; deduces a std::initializer_list<int>, which now deduces to int.

修改了auto推导,当使用统一的初始语法。以前,auto x{3} 推导为std::initializer_list<int>, 而现在为int

auto x1 {1, 2, 3}; // error: not a single element
auto x2 = {1, 2, 3}; // x2 is std::initializer_list<int>
auto x3 {3}; // x3 is int
auto x4 {3.0}; // x4 is double

constexpr lambda

Compile-time lambdas using constexpr.

auto identity = [](int n) constexpr { return n; };
static_assert(identity(123) == 123);
constexpr auto add = [](int x, int y) {
  auto L = [=] { return x; };
  auto R = [=] { return y; };
  return [=] { return L() + R(); };
};

static_assert(add(1, 2)() == 3);
constexpr int addOne(int n) {
  return [n] { return n + 1; }();
}

static_assert(addOne(1) == 2);

Lambda capture this by value

Capturing this in a lambda’s environment was previously reference-only. An example of where this is problematic is asynchronous code using callbacks that require an object to be available, potentially past its lifetime. *this (C++17) will now make a copy of the current object, while this (C++11) continues to capture by reference.

以前this在lambda环境中的捕获只提供引用。如果在异步回调场景中,它就会有问题,对象生命周期过时。(c++17)可以拷贝当前对象(而 在C++11中只能引用)。

struct MyObj {
  int value {123};
  auto getValueCopy() {
    return [*this] { return value; };
  }
  auto getValueRef() {
    return [this] { return value; };
  }
};
MyObj mo;
auto valueCopy = mo.getValueCopy();
auto valueRef = mo.getValueRef();
mo.value = 321;
valueCopy(); // 123
valueRef(); // 321

Inline variables

The inline specifier can be applied to variables as well as to functions. A variable declared inline has the same semantics as a function declared inline.

inline可以像应用于函数一样应用于变量,一个inline变量和inline函数一样有同样的意思。

// Disassembly example using compiler explorer.
struct S { int x; };
inline S x1 = S{321}; // mov esi, dword ptr [x1]
                      // x1: .long 321

S x2 = S{123};        // mov eax, dword ptr [.L_ZZ4mainE2x2]
                      // mov dword ptr [rbp - 8], eax
                      // .L_ZZ4mainE2x2: .long 123

It can also be used to declare and define a static member variable, such that it does not need to be initialized in the source file.

inline同样可以用于static成员变量,这样就不用在cpp文件中初始化了。

struct S {
  S() : id{count++} {}
  ~S() { count--; }
  int id;
  static inline int count{0}; // declare and initialize count to 0 within the class
};

Nested namespaces

嵌套命名空间

Using the namespace resolution operator to create nested namespace definitions.

namespace A {
  namespace B {
    namespace C {
      int i;
    }
  }
}
//可以写成这样
namespace A::B::C {
  int i;
}

Structured bindings

A proposal for de-structuring initialization, that would allow writing auto [ x, y, z ] = expr; where the type of expr was a tuple-like object, whose elements would be bound to the variables x, y, and z (which this construct declares). Tuple-like objects include std::tuple, std::pair, std::array, and aggregate structures.

一个关于解构的提议,可以允许写 auto [ x, y, z ] = expr; 其中expr的类型为tuple-like对象,这种对象里的元素会赋值给x,y,z。tuple-like对象包括std::tuple, std::pair, std::array, 和聚合结构.

using Coordinate = std::pair<int, int>;
Coordinate origin() {
  return Coordinate{0, 0};
}

const auto [ x, y ] = origin();
x; // == 0
y; // == 0
std::unordered_map<std::string, int> mapping {
  {"a", 1},
  {"b", 2},
  {"c", 3}
};

// Destructure by reference.
for (const auto& [key, value] : mapping) {
  // Do something with key and value
}

Selection statements with initializer

New versions of the if and switch statements which simplify common code patterns and help users keep scopes tight.

if和switch语句内可以初始化变量。

{
  std::lock_guard<std::mutex> lk(mx);
  if (v.empty()) v.push_back(val);
}
// vs.
if (std::lock_guard<std::mutex> lk(mx); v.empty()) {
  v.push_back(val);
}
Foo gadget(args);
switch (auto s = gadget.status()) {
  case OK: gadget.zip(); break;
  case Bad: throw BadFoo(s.message());
}
// vs.
switch (Foo gadget(args); auto s = gadget.status()) {
  case OK: gadget.zip(); break;
  case Bad: throw BadFoo(s.message());
}

constexpr if

Write code that is instantiated depending on a compile-time condition.

编写编译时实例化代码

template <typename T>
constexpr bool isIntegral() {
  if constexpr (std::is_integral<T>::value) {
    return true;
  } else {
    return false;
  }
}
static_assert(isIntegral<int>() == true);
static_assert(isIntegral<char>() == true);
static_assert(isIntegral<double>() == false);
struct S {};
static_assert(isIntegral<S>() == false);

UTF-8 character literals

A character literal that begins with u8 is a character literal of type char. The value of a UTF-8 character literal is equal to its ISO 10646 code point value.

char x = u8'x';

Direct list initialization of enums

Enums can now be initialized using braced syntax.

enums可以用大括号来初始化

enum byte : unsigned char {};
byte b {0}; // OK
byte c {-1}; // ERROR
byte d = byte{1}; // OK
byte e = byte{256}; // ERROR

fallthrough, nodiscard, maybe_unused attributes

[[fallthrough]] indicates to the compiler that falling through in a switch statement is intended behavior.

告诉编译器,switch中的case fallthrough是有意而为之,不用warning。

switch (n) {
  case 1: [[fallthrough]]
    // ...
  case 2:
    // ...
    break;
}

[[nodiscard]] issues a warning when either a function or class has this attribute and its return value is discarded.

函数或者类可以有些属性,表明返回值不能被丢弃。

[[nodiscard]] bool do_something() {
  return is_success; // true for success, false for failure
}

do_something(); // warning: ignoring return value of 'bool do_something()',
                // declared with attribute 'nodiscard'
// Only issues a warning when `error_info` is returned by value.
struct [[nodiscard]] error_info {
  // ...
};

error_info do_something() {
  error_info ei;
  // ...
  return ei;
}

do_something(); // warning: ignoring returned value of type 'error_info',
                // declared with attribute 'nodiscard'

[[maybe_unused]] indicates to the compiler that a variable or parameter might be unused and is intended.

告诉编译器变量或参数可能不会用到,不用warning。

void my_callback(std::string msg, [[maybe_unused]] bool error) {
  // Don't care if `msg` is an error message, just log it.
  log(msg);
}

C++17 Library Features

std::variant

The class template std::variant represents a type-safe union. An instance of std::variant at any given time holds a value of one of its alternative types (it’s also possible for it to be valueless).

std::variant代表类型安全的union 模板类。一个std::variant实例一次只装载它候选类型中的一个值,也有可能没有值。

std::variant<int, double> v {12};
std::get<int>(v); // == 12
std::get<0>(v); // == 12
v = 12.0;
std::get<double>(v); // == 12.0
std::get<1>(v); // == 12.0

std::optional

The class template std::optional manages an optional contained value, i.e. a value that may or may not be present. A common use case for optional is the return value of a function that may fail.

std::optional管理一个可能包含的值,可能有,可能无。一个通常的情况是当函数条件失败时,无返回值。

std::optional<std::string> create(bool b) {
  if (b) {
    return "Godzilla";
  } else {
    return {};
  }
}

create(false).value_or("empty"); // == "empty"
create(true).value(); // == "Godzilla"
// optional-returning factory functions are usable as conditions of while and if
if (auto str = create(true)) {
  // ...
}

std::any

A type-safe container for single values of any type.

一个可以为任何类型的单值的 类型安全容器。

std::any x {5};
x.has_value() // == true
std::any_cast<int>(x) // == 5
std::any_cast<int&>(x) = 10;
std::any_cast<int>(x) // == 10

std::string_view

A non-owning reference to a string. Useful for providing an abstraction on top of strings (e.g. for parsing).

一个string的非拥有引用,用于在字符串上提供抽象(例如解析)

// Regular strings.
std::string_view cppstr {"foo"};
// Wide strings.
std::wstring_view wcstr_v {L"baz"};
// Character arrays.
char array[3] = {'b', 'a', 'r'};
std::string_view array_v(array, std::size(array));
std::string str {"   trim me"};
std::string_view v {str};
v.remove_prefix(std::min(v.find_first_not_of(" "), v.size()));
str; //  == "   trim me"
v; // == "trim me"

std::invoke

Invoke a Callable object with parameters. Examples of Callable objects are std::function or std::bind where an object can be called similarly to a regular function.

template <typename Callable>
class Proxy {
  Callable c;
public:
  Proxy(Callable c): c(c) {}
  template <class... Args>
  decltype(auto) operator()(Args&&... args) {
    // ...
    return std::invoke(c, std::forward<Args>(args)...);
  }
};
auto add = [](int x, int y) {
  return x + y;
};
Proxy<decltype(add)> p {add};
p(1, 2); // == 3

std::apply

Invoke a Callable object with a tuple of arguments.

auto add = [](int x, int y) {
  return x + y;
};
std::apply(add, std::make_tuple(1, 2)); // == 3

std::filesystem

The new std::filesystem library provides a standard way to manipulate files, directories, and paths in a filesystem.

Here, a big file is copied to a temporary path if there is available space:

const auto bigFilePath {"bigFileToCopy"};
if (std::filesystem::exists(bigFilePath)) {
  const auto bigFileSize {std::filesystem::file_size(bigFilePath)};
  std::filesystem::path tmpPath {"/tmp"};
  if (std::filesystem::space(tmpPath).available > bigFileSize) {
    std::filesystem::create_directory(tmpPath.append("example"));
    std::filesystem::copy_file(bigFilePath, tmpPath.append("newFile"));
  }
}

std::byte

The new std::byte type provides a standard way of representing data as a byte. Benefits of using std::byte over char or unsigned char is that it is not a character type, and is also not an arithmetic type; while the only operator overloads available are bitwise operations.

std::byte a {0};
std::byte b {0xFF};
int i = std::to_integer<int>(b); // 0xFF
std::byte c = a & b;
int j = std::to_integer<int>(c); // 0

Note that std::byte is simply an enum, and braced initialization of enums become possible thanks to direct-list-initialization of enums.

Splicing for maps and sets

Moving nodes and merging containers without the overhead of expensive copies, moves, or heap allocations/deallocations.

移动结点和合并容器没有额外的,代价高的开销:拷贝,移动,堆的申请/释放。

Moving elements from one map to another:

移动map的元素到另一个map:

std::map<int, string> src { {1, "one"}, {2, "two"}, {3, "buckle my shoe"} };
std::map<int, string> dst { {3, "three"} };
dst.insert(src.extract(src.find(1))); // Cheap remove and insert of { 1, "one" } from `src` to `dst`.
dst.insert(src.extract(2)); // Cheap remove and insert of { 2, "two" } from `src` to `dst`.
// dst == { { 1, "one" }, { 2, "two" }, { 3, "three" } };

Inserting an entire set:

插入整个set:

std::set<int> src {1, 3, 5};
std::set<int> dst {2, 4, 5};
dst.merge(src);
// src == { 5 }
// dst == { 1, 2, 3, 4, 5 }

Inserting elements which outlive the container:

auto elementFactory() {
  std::set<...> s;
  s.emplace(...);
  return s.extract(s.begin());
}
s2.insert(elementFactory());

Changing the key of a map element:

修改key:

std::map<int, string> m { {1, "one"}, {2, "two"}, {3, "three"} };
auto e = m.extract(2);
e.key() = 4;
m.insert(std::move(e));
// m == { { 1, "one" }, { 3, "three" }, { 4, "two" } }

Parallel algorithms

并行算法

Many of the STL algorithms, such as the copy, find and sort methods, started to support the parallel execution policies: seq, par and par_unseq which translate to “sequentially”, “parallel” and “parallel unsequenced”.

std::vector<int> longVector;
// Find element using parallel execution policy
auto result1 = std::find(std::execution::par, std::begin(longVector), std::end(longVector), 2);
// Sort elements using sequential execution policy
auto result2 = std::sort(std::execution::seq, std::begin(longVector), std::end(longVector));

C++14 Language Features

Binary literals

Binary literals provide a convenient way to represent a base-2 number. It is possible to separate digits with '.

0b110 // == 6
0b1111'1111 // == 255

Generic lambda expressions

C++14 now allows the auto type-specifier in the parameter list, enabling polymorphic lambdas.

c++14允许lambdas表达式的参数列表使用auto类型,这样来支持多态。

auto identity = [](auto x) { return x; };
int three = identity(3); // == 3
std::string foo = identity("foo"); // == "foo"

Lambda capture initializers

This allows creating lambda captures initialized with arbitrary expressions. The name given to the captured value does not need to be related to any variables in the enclosing scopes and introduces a new name inside the lambda body. The initializing expression is evaluated when the lambda is created (not when it is invoked).

这允许创建用任意表达式初始化的lambda捕获。为捕获的值指定的名称不需要与封闭范围中的任何变量相关,并在lambda主体中引入一个新名称。初始化表达式在lambdacreated时计算(而不是在invoked时)

类似lua中的闭包。

int factory(int i) { return i * 10; }
auto f = [x = factory(2)] { return x; }; // returns 20

auto generator = [x = 0] () mutable {
  // this would not compile without 'mutable' as we are modifying x on each call
  return x++;
};
auto a = generator(); // == 0
auto b = generator(); // == 1
auto c = generator(); // == 2

Because it is now possible to move (or forward) values into a lambda that could previously be only captured by copy or reference we can now capture move-only types in a lambda by value. Note that in the below example the p in the capture-list of task2 on the left-hand-side of = is a new variable private to the lambda body and does not refer to the original p.

因为现在可以将值转移、或转发给lambda,以前只能通过拷贝或引用的方式来捕获。

auto p = std::make_unique<int>(1);

auto task1 = [=] { *p = 5; }; // ERROR: std::unique_ptr 不能拷贝
// vs.
auto task2 = [p = std::move(p)] { *p = 5; }; // OK: p is move-constructed into the closure object
// the original p is empty after task2 is created

Using this reference-captures can have different names than the referenced variable.

使用此引用捕获可以具有与引用变量不同的名称。

auto x = 1;
auto f = [&r = x, x = x * 10] {
  ++r;
  return r + x;
};
f(); // sets x to 2 and returns 12

Return type deduction

Using an auto return type in C++14, the compiler will attempt to deduce the type for you. With lambdas, you can now deduce its return type using auto, which makes returning a deduced reference or rvalue reference possible.

在c++14中返回值用auto,编译器将为你推导类型。在lambdas中,你现在能用auto来推导返回值类型,lambdas可能返回引用或右值性和特 。

// Deduce return type as `int`.
auto f(int i) {
 return i;
}
template <typename T>
auto& f(T& t) {
  return t;
}

// Returns a reference to a deduced type.
auto g = [](auto& x) -> auto& { return f(x); };
int y = 123;
int& z = g(y); // reference to `y`

decltype(auto)

The decltype(auto) type-specifier also deduces a type like auto does. However, it deduces return types while keeping their references and cv-qualifiers, while auto will not.

decltype(auto)也像auto那样类型推导。但是,推导出的类型会保留它们的引用和const属性,然而auto是不会的。

const int x = 0;
auto x1 = x; // int
decltype(auto) x2 = x; // const int
int y = 0;
int& y1 = y;
auto y2 = y1; // int
decltype(auto) y3 = y1; // int&
int&& z = 0;
auto z1 = std::move(z); // int
decltype(auto) z2 = std::move(z); // int&&
// Note: Especially useful for generic code!

// Return type is `int`.
auto f(const int& i) {
 return i;
}

// Return type is `const int&`.
decltype(auto) g(const int& i) {
 return i;
}

int x = 123;
static_assert(std::is_same<const int&, decltype(f(x))>::value == 0);
static_assert(std::is_same<int, decltype(f(x))>::value == 1);
static_assert(std::is_same<const int&, decltype(g(x))>::value == 1);

See also: decltype.

Relaxing constraints on constexpr functions

解放constexpr 函数的约束

In C++11, constexpr function bodies could only contain a very limited set of syntaxes, including (but not limited to): typedefs, usings, and a single return statement. In C++14, the set of allowable syntaxes expands greatly to include the most common syntax such as if statements, multiple returns, loops, etc.

在c++11中,constexpr函数体只能包括有限的语法,但在C++14,可以允许的语法得到很大的扩充,例如:加入了if语句,多个return,loops等等。

constexpr int factorial(int n) {
  if (n <= 1) {
    return 1;
  } else {
    return n * factorial(n - 1);
  }
}
factorial(5); // == 120

Variable templates

C++14 allows variables to be templated:

template<class T>
constexpr T pi = T(3.1415926535897932385);
template<class T>
constexpr T e  = T(2.7182818284590452353);

[[deprecated]] attribute

弃用属性

C++14 introduces the [[deprecated]] attribute to indicate that a unit (function, class, etc) is discouraged and likely yield compilation warnings. If a reason is provided, it will be included in the warnings.

如果使用的带弃用属性的函数,类,等,编译时将warning。

[[deprecated]]
void old_method();

[[deprecated("Use new_method instead")]]
void legacy_method();

C++14 Library Features

User-defined literals for standard library types

为校准库类型定义用户字面量

New user-defined literals for standard library types, including new built-in literals for chrono and basic_string. These can be constexpr meaning they can be used at compile-time. Some uses for these literals include compile-time integer parsing, binary literals, and imaginary number literals.

using namespace std::chrono_literals;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440

Compile-time integer sequences

The class template std::integer_sequence represents a compile-time sequence of integers. There are a few helpers built on top:

  • std::make_integer_sequence<T, N> - creates a sequence of 0, ..., N - 1 with type T.
  • std::index_sequence_for<T...> - converts a template parameter pack into an integer sequence.

Convert an array into a tuple:

template<typename Array, std::size_t... I>
decltype(auto) a2t_impl(const Array& a, std::integer_sequence<std::size_t, I...>) {
  return std::make_tuple(a[I]...);
}

template<typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
decltype(auto) a2t(const std::array<T, N>& a) {
  return a2t_impl(a, Indices());
}

std::make_unique

std::make_unique is the recommended way to create instances of std::unique_ptrs due to the following reasons:

推荐使用std::make_unique 创建std::unique_ptr的实例,下面是具体原因:

  • Avoid having to use the new operator.避免不得不使用new操作符
  • Prevents code repetition when specifying the underlying type the pointer shall hold.在指定指针应保持的基础类型时防止代码重复。
  • Most importantly, it provides exception-safety. Suppose we were calling a function foo like so:最重要的是提供异常安全。想象下我们调用如下的foo函数:
foo(std::unique_ptr<T>{new T{}}, function_that_throws(), std::unique_ptr<T>{new T{}});

The compiler is free to call new T{}, then function_that_throws(), and so on… Since we have allocated data on the heap in the first construction of a T, we have introduced a leak here. With std::make_unique, we are given exception-safety:

foo(std::make_unique<T>(), function_that_throws(), std::make_unique<T>());

See the section on smart pointers for more information on std::unique_ptr and std::shared_ptr.

C++11 Language Features

Move semantics 转移语义

Moving an object means to transfer ownership of some resource it manages to another object.

转移一个对象表示将它所管理资源的所有权转移给另外一个对象。

The first benefit of move semantics is performance optimization. When an object is about to reach the end of its lifetime, either because it’s a temporary or by explicitly calling std::move, a move is often a cheaper way to transfer resources. For example, moving a std::vector is just copying some pointers and internal state over to the new vector – copying would involve having to copy every single contained element in the vector, which is expensive and unnecessary if the old vector will soon be destroyed.

转移最先获得的益处是性能的提高。当一个对象到了的生命期结束,不管是因为它是临时的或是显式调用std::move,转移资源move的代价更小。例如,move转移一个std::vector仅仅是拷贝一些指针和内部状态到新的vector。拷贝的话需要拷贝vector包含的每个元素,显然如果旧的vector的生命期到了,这些拷贝构造每个元素是不必要的。

Moves also make it possible for non-copyable types such as std::unique_ptrs (smart pointers) to guarantee at the language level that there is only ever one instance of a resource being managed at a time, while being able to transfer an instance between scopes.

转移对于non-copyable的类型也是可用的,例如std::unique_ptr。

See the sections on: rvalue references, special member functions for move semantics, std::move, std::forward, forwarding references.

Rvalue references右值引用

C++11 introduces a new reference termed the rvalue reference. An rvalue reference to T, which is a non-template type parameter (such as int, or a user-defined type), is created with the syntax T&&. Rvalue references only bind to rvalues.

Type deduction with lvalues and rvalues:

int x = 0; // `x` is an lvalue of type `int`
int& xl = x; // `xl` is an lvalue of type `int&`
int&& xr = x; // compiler error -- `x` is an lvalue
int&& xr2 = 0; // `xr2` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`

void f(int& x) {}
void f(int&& x) {}

f(x);  // calls f(int&)
f(xl); // calls f(int&)
f(3);  // calls f(int&&)
f(std::move(x)) // calls f(int&&)

f(xr2);           // calls f(int&)
f(std::move(xr2)) // calls f(int&& x)

See also: std::move, std::forward, forwarding references.

Forwarding references 转寄引用

Also known (unofficially) as universal references. A forwarding reference is created with the syntax T&& where T is a template type parameter, or using auto&&. This enables perfect forwarding: the ability to pass arguments while maintaining their value category (e.g. lvalues stay as lvalues, temporaries are forwarded as rvalues).

也称为(非正式)通用引用。

Forwarding references allow a reference to bind to either an lvalue or rvalue depending on the type. Forwarding references follow the rules of reference collapsing:

  • T& & becomes T&
  • T& && becomes T&
  • T&& & becomes T&
  • T&& && becomes T&&

还有很多….