For faster navigation, this Iframe is preloading the Wikiwand page for C++17.

C++17

C++17 是C++标准委员会于2017年制定的标准(ISO/IEC 14882),接替C++14标准,后被C++20标准取代。

历史

在C++标准委员会制定此次的三年期标准之前,C++17标准的发布日期是不确定的。在这段日期中,C++17草案(draft)也被称为C++1z,正如C++11草案名C++0x或C++1x,C++14草案名C++1y。C++17标准于2017年3月完成国际标准草案(Draft International Standard DIS)阶段,并于同年12月完成最终标准发布。17标准对C++语法进行了加强,并对标准模板库进行了些许修改,如给<algorithm>库中算法增加了执行策略execution以支持并行计算。

新功能

  • 静态断言static_assert无需提供出错资讯[1]
  • 移除 trigraphs[2][3]
  • 具有模板形式的模板(template template)参数允许使用 typename(之前仅允许使用 class)[4]
  • std::uncaught_exceptions 取代 std::uncaught_exception[5][6]
  • 变长参数模板的Folding运算[6][7]
  • 容器存取操作表示方法的统一化(Uniform container access)[8][9]
  • 连续型迭代器(Contiguous Iterators)[8][10]
  • 新增特殊数学函数[11]
  • 引进Library Fundamentals TS I中的内容[12]

类模板的模板参数推导

模板类构造函数能自动推导模板参数的类型(Class template argument deduction, CTAD)。例如允许以pair(5.0, false) 取代pair<double,bool>(5.0, false)

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>

任何指定变量或变量模板的初始化的声明,其声明的类型是类模板(可能是 cv 限定的)。如std::pair p(2, 4.5); // deduces to std::pair<int, double> p(2, 4.5); new表达式,如template<class T>struct A{ A(T, T);}; auto y = new A{1, 2}; // allocated type is A<int>

用auto声明非类型模板参数

依据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>();

折叠表达式

折叠表达式在运算符上执行模板参数包的展开计算。

  • 单元折叠:形如 (... op e) or (e op ...)表达式,其中 op 是折叠运算符,e 是未展开的模板参数包。
  • 二元折叠:形如(e1 op ... op e2)表达式, 其中 op 是折叠运算符,e1或e2 是未展开的模板参数包。
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

大括号初始化列表的新的自动推导规则

以前auto x {3}; 将被推导为std::initializer_list<int>, 现在推导为int[13][14]

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

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函数中按值捕获this指针

以前this指针在lambda函数只能按引用捕获。现在 *this 捕获对象的副本,而 this (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可用于函数声明,现在也可以用于变量声明,表示函数或定义可定义(但内容必须完全相同)多次。这允许在头文件中定义一个内联变量。

  • 内联的函数或变量必须在编译单元内可达(但不一定在访问点之前)
  • 在每个编译单元中都是内联声明的
  • 内联变量必须是静态存储器(类静态成员或命名空间作用域变量),不能用于块作用域变量或函数作用域变量。
struct S {
  S() : id{count++} {}
  ~S() { count--; }
  int id;
  static inline int count{0}; // declare and initialize count to 0 within the class
};

嵌套的命名空间

如下:[14][15]

namespace A {
  namespace B {
    namespace C {
      int i;
    }
  }
}

//The code above can be written like this:

namespace A::B::C {
  int i;
}

结构化绑定

变量定义初始化时,允许形如 auto [ x, y, z ] = expr;,其中expr的“元组类似的对象”包括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
}

带初始化的选择语句

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

//适用场景1:简化模版偏特化的写法
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);

//适用场景2:编写变参模版函数
template<int N, int...Ns>
int Sum()
{
  if constexpr(0 == sizeof...(Ns))
    return N;
  else
    return N+Sum<Ns...> ();
}

//使用场景3:替代enable_if。编写模板函数时,经常要使用enable_if语句来进行静态类型检查,保证模板输入的类型满足某种要求
template <typename T>
bool IsOdd(T input){
 if constexpr (std::is_integral<T>::value) //
   return static_cast<bool>(input % 2);
}

utf8字面量

如下:[6][16]

char x = u8'x';

枚举类型变量直接用大括号初始化

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特性

C++17 引入了3个特性(attribute): fallthrough, nodiscard, maybe_unused.

//fallthrough指示编译器直通一个switch语句:
switch (n) {
  case 1: 
    // ...
    [[fallthrough]]
  case 2:
    // ...
    break;
}

//nodiscard引发一个警告,当具有该特性的函数或类的返回值被忽略:
[[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告诉编译器变量或参数可能未被使用:
void my_callback(std::string msg, [[maybe_unused]] bool error) {
  // Don't care if `msg` is an error message, just log it.
  log(msg);
}

__has_include运算符

__has_include运算符用在#if 和 #elif表达式,以检查是否一个头文件或源文件可否包含。

//下例有两个相同作用的头文件,如果主文件不能包含, 就使用backup/experimental头文件:
#ifdef __has_include
#  if __has_include(<optional>)
#    include <optional>
#    define have_optional 1
#  elif __has_include(<experimental/optional>)
#    include <experimental/optional>
#    define have_optional 1
#    define experimental_optional
#  else
#    define have_optional 0
#  endif
#endif

//也可以用在不同位置、不同名字但具有相同地位的头文件的包含:
#ifdef __has_include
#  if __has_include(<OpenGL/gl.h>)
#    include <OpenGL/gl.h>
#    include <OpenGL/glu.h>
#  elif __has_include(<GL/gl.h>)
#    include <GL/gl.h>
#    include <GL/glu.h>
#  else
#    error No suitable OpenGL headers found.
# endif
#endif

std::variant

std::variant模板类实现了类型安全的union

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

std::optional类模板可能包含一个值或者可能不包含值:

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

create(false).has_value();//返回true或false
*create(false);//返回值;如果无值,则返回T()
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

std::any模板类是类型安全的包含任何类型的值:

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

std::string_view类是不拥有字符串的情况可以读取其值:[17]

// 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::string_view不提供c_str()成员函数。

std::invoke

std::invoke用于调用“可调用对象”。

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

std::apply把std::tuple参数应用于可调用对象:

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

std::filesystem

std::filesystem可操作文件、目录、路径:[18]

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

std::byte类型既不是字符类型,也不是算术类型,实际上是枚举类型:

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

maps 和 sets 上更有效率地移动节点

零开销的节点操作:[19][8]

//Moving elements from one map to another:
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:
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:
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" } }

并行算法

许多STL算法,如copy, find 和 sort,支持并行执行政策:seq, par, par_unseq, unseq分别表示"sequentially", "parallel", "parallel unsequenced", "unsequenced".[20]

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));

注释

  1. ^ N3928: Extending static_assert, v2 (Walter E. Brown) (PDF). [2015-07-16]. (原始内容存档 (PDF)于2015-08-11). 
  2. ^ N3981: Removing trigraphs??! (Richard Smith). 2014-05-06 [2015-07-16]. (原始内容存档于2018-07-09). 
  3. ^ IBM comment on preparing for a Trigraph-adverse future in C++17页面存档备份,存于互联网档案馆), IBM paper N4210, 2014-10-10. Authors: Michael Wong, Hubert Tong, Rajan Bhakta, Derek Inglis
  4. ^ N4051: Allow typename in a template template parameter (Richard Smith). [2015-07-16]. (原始内容存档于2015-08-11). 
  5. ^ N4259: Wording for std::uncaught_exceptions (Herb Sutter) (PDF). [2015-07-16]. (原始内容 (PDF)存档于2014-11-29). 
  6. ^ 6.0 6.1 6.2 New core language papers adopted for C++17. [2015-07-16]. (原始内容存档于2015-04-27). 
  7. ^ N4295: Folding expressions (Andrew Sutton, Richard Smith). [2015-07-16]. (原始内容存档于2015-04-04). 
  8. ^ 8.0 8.1 8.2 New standard library papers adopted for C++17. [2015-07-16]. (原始内容存档于2014-11-29). 
  9. ^ N4280: Non-member size() and more (Riccardo Marcangelo) (PDF). [2015-07-16]. (原始内容存档 (PDF)于2015-03-09). 
  10. ^ N4284: Contiguous Iterators (Jens Maurer). [2015-07-16]. (原始内容存档于2014-11-29). 
  11. ^ Mathematical Special Functions for C++17, v5 (PDF). [2016-08-02]. (原始内容存档 (PDF)于2016-04-05). 
  12. ^ Adopt Library Fundamentals V1 TS Components for C++17 (R1). [2016-08-02]. (原始内容存档于2016-04-05). 
  13. ^ N3922: New Rules for auto deduction from braced-init-list (James Dennett). [2015-07-16]. (原始内容存档于2015-08-10). 
  14. ^ 14.0 14.1 Updates to my trip report. [2015-07-16]. (原始内容存档于2015-03-19). 
  15. ^ N4230: Nested namespace definition (Robert Kawulak, Andrew Tomazos). [2015-07-16]. (原始内容存档于2015-08-03). 
  16. ^ N4267: Adding u8 character literals (Richard Smith). [2015-07-16]. (原始内容存档于2015-10-28). 
  17. ^ std::basic_string_view - cppreference.com. en.cppreference.com. [2016-06-23]. (原始内容存档于2016-06-17). 
  18. ^ Filesystem Library Proposal (Beman Dawes). [2016-08-02]. (原始内容存档于2016-07-20). 
  19. ^ N4279: Improved insertion interface for unique-key maps (Thomas Köppe). [2015-07-16]. (原始内容存档于2015-04-27). 
  20. ^ The Parallelism TS Should be Standardized. [2016-08-02]. (原始内容存档于2016-04-05). 

另见

{{bottomLinkPreText}} {{bottomLinkText}}
C++17
Listen to this article

This browser is not supported by Wikiwand :(
Wikiwand requires a browser with modern capabilities in order to provide you with the best reading experience.
Please download and use one of the following browsers:

This article was just edited, click to reload
This article has been deleted on Wikipedia (Why?)

Back to homepage

Please click Add in the dialog above
Please click Allow in the top-left corner,
then click Install Now in the dialog
Please click Open in the download dialog,
then click Install
Please click the "Downloads" icon in the Safari toolbar, open the first download in the list,
then click Install
{{::$root.activation.text}}

Install Wikiwand

Install on Chrome Install on Firefox
Don't forget to rate us

Tell your friends about Wikiwand!

Gmail Facebook Twitter Link

Enjoying Wikiwand?

Tell your friends and spread the love:
Share on Gmail Share on Facebook Share on Twitter Share on Buffer

Our magic isn't perfect

You can help our automatic cover photo selection by reporting an unsuitable photo.

This photo is visually disturbing This photo is not a good choice

Thank you for helping!


Your input will affect cover photo selection, along with input from other users.

X

Get ready for Wikiwand 2.0 🎉! the new version arrives on September 1st! Don't want to wait?