C++20 包含以下新的語言功能:
C++20 包含以下新函式庫功能:
C++17 包含以下新的語言功能:
C++17 包含以下新函式庫功能:
C++14 包含以下新的語言功能:
C++14 包含以下新函式庫功能:
C++11 包含以下新的語言功能:
C++11 包含以下新函式庫功能:
注意:雖然這些範例說明如何在基本層級上使用協程,但編譯程式碼時還會發生更多事情。這些範例並不意味著完整覆蓋 C++20 的協程。由於標準庫尚未提供
generator
和task
類,因此我使用 cppcoro 庫來編譯這些範例。
協程是可以暫停和恢復執行的特殊函數。若要定義協程,函數體內必須存在co_return
、 co_await
或co_yield
關鍵字。 C++20 的協程是無堆疊的;除非被編譯器最佳化,否則它們的狀態將分配在堆上。
協程的一個例子是生成器函數,它在每次呼叫時產生(即生成)一個值:
generator< int > range ( int start, int end) {
while (start < end) {
co_yield start;
start++;
}
// Implicit co_return at the end of this function:
// co_return;
}
for ( int n : range( 0 , 10 )) {
std::cout << n << std::endl;
}
上述range
產生器函數產生從start
到end
(不包含)的值,每個迭代步驟都會產生儲存在start
中的目前值。生成器在每次range
呼叫中維護其狀態(在本例中,呼叫是針對 for 循環中的每次迭代)。 co_yield
接受給定的表達式,產生(即返回)其值,並在該點掛起協程。恢復後,執行會在co_yield
之後繼續。
協程的另一個例子是任務,它是在等待任務時執行的非同步計算:
task< void > echo (socket s) {
for (;;) {
auto data = co_await s. async_read ();
co_await async_write (s, data);
}
// Implicit co_return at the end of this function:
// co_return;
}
在此範例中,引入了co_await
關鍵字。如果您正在等待的事物(在本例中為讀取或寫入)未準備好,此關鍵字採用一個表達式並暫停執行,否則您將繼續執行。 (請注意,在幕後, co_yield
使用co_await
。)
使用任務來延遲評估值:
task< int > calculate_meaning_of_life () {
co_return 42 ;
}
auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
概念被命名為約束類型的編譯時謂詞。它們採用以下形式:
template < template-parameter-list >
concept concept-name = constraint-expression;
其中, constraint-expression
計算結果為 constexpr 布林值。約束應該對語義要求進行建模,例如類型是數字還是可散列。如果給定類型不滿足其所綁定的概念(即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>;
有多種句法形式可以強化概念:
// 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> () {
// ...
};
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;
}
請注意, requires
表達式中的參數清單是可選的。 requires
表達式中的每個要求都是以下之一:
template < typename T>
concept callable = requires (T f) { f (); };
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.
template < typename T>
concept C = requires(T x) {
{*x} -> std::convertible_to< 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 } -> std::convertible_to<T>; // the type of the expression `x * 1` is convertible to `T`
};
requires
關鍵字表示,指定附加約束(例如本地參數參數上的約束)。 template < typename T>
concept C = requires(T x) {
requires std::same_as< sizeof (x), size_t >;
};
另請參閱:概念庫。
C++20 引入了太空船運算子 ( <=>
) 作為編寫比較函數的新方法,可減少樣板檔案並協助開發人員定義更清晰的比較語意。定義三向比較運算子將自動產生其他比較運算子函數(即==
、 !=
、 <
等)。
引入三種排序:
std::strong_ordering
:強排序區分相等的項目(相同和可互換)。提供less
、 greater
、 equivalent
和equal
排序。比較範例:在清單中搜尋特定值、整數值、區分大小寫的字串。std::weak_ordering
:弱排序區分等值(不相同,但為了比較目的可以互換)。提供less
、 greater
和equivalent
排序。比較範例:不區分大小寫的字串、排序、比較類別的部分但非全部可見成員。std::partial_ordering
:部分排序遵循弱排序的相同原則,但包括無法排序的情況。提供less
、 greater
、 equivalent
和unordered
排序。比較範例:浮點數值(例如NaN
)。預設的三向比較運算子進行逐成員比較:
struct foo {
int a;
bool b;
char c;
// Compare `a` first, then `b`, then `c` ...
auto operator <=>( const foo&) const = default ;
};
foo f1{ 0 , false , ' a ' }, f2{ 0 , true , ' b ' };
f1 < f2; // == true
f1 == f2; // == false
f1 >= f2; // == false
您也可以定義自己的比較:
struct foo {
int x;
bool b;
char c;
std::strong_ordering operator <=>( const foo& other) const {
return x <=> other. x ;
}
};
foo f1{ 0 , false , ' a ' }, f2{ 0 , true , ' b ' };
f1 < f2; // == false
f1 == f2; // == true
f1 >= f2; // == true
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
在 lambda 表達式中使用熟悉的模板語法。
auto f = []< typename T>(std::vector<T> v) {
// ...
};
此功能簡化了常見的程式碼模式,有助於保持範圍緊湊,並為常見的生命週期問題提供了優雅的解決方案。
for ( auto v = std::vector{ 1 , 2 , 3 }; auto & e : v) {
std::cout << e;
}
// prints "123"
向優化器提供一個提示:標記語句有很高的執行機率。
switch (n) {
case 1 :
// ...
break ;
[[likely]] case 2 : // n == 2 is considered to be arbitrarily more
// ... // likely than any other value of n
break ;
}
如果可能/不可能屬性之一出現在 if 語句的右括號之後,則表示該分支可能/不可能執行其子語句(主體)。
int random = get_random_number_between_x_and_y( 0 , 3 );
if (random > 0 ) [[likely]] {
// body of if statement
// ...
}
它也可以應用於迭代語句的子語句(主體)。
while (unlikely_truthy_condition) [[unlikely]] {
// body of while statement
// ...
}
現在不建議使用[=]
在 lambda 捕獲中隱式捕獲this
;偏好使用[=, this]
或[=, *this]
明確擷取。
struct int_value {
int n = 0 ;
auto getter_fn () {
// BAD:
// return [=]() { return n; };
// GOOD:
return [=, * this ]() { return n; };
}
};
類別現在可以在非類型模板參數中使用。作為模板參數傳入的物件具有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
並在編譯時求值。 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(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
與constexpr
函數類似,但帶有consteval
說明符的函數必須產生常數。這些稱為immediate functions
。
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
將枚舉的成員納入範圍以提高可讀性。前:
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 " ;
}
}
後:
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 " ;
}
}
按值捕獲參數包:
template < typename ... Args>
auto f (Args&&... args){
// BY VALUE:
return [... args = std::forward<Args>(args)] {
// ...
};
}
透過引用捕獲參數包:
template < typename ... Args>
auto f (Args&&... args){
// BY REFERENCE:
return [&... args = std::forward<Args>(args)] {
// ...
};
}
提供用於表示 UTF-8 字串的標準類型。
char8_t utf8_str[] = u8" u0123 " ;
constinit
說明符要求變數必須在編譯時初始化。
const char * g () { return " dynamic initialization " ; }
constexpr const char * f ( bool p) { return p ? " constant initializer " : g (); }
constinit const char * c = f( true ); // OK
constinit const char * d = g( false ); // ERROR: `g` is not constexpr, so `d` cannot be evaluated at compile-time.
如果可變參數巨集非空,則透過評估給定參數來協助支援可變參數巨集。
# define F (...) f( 0 __VA_OPT__ (,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F() // replaced by f(0)
標準庫也提供了概念來建構更複雜的概念。其中一些包括:
核心語言概念:
same_as
- 指定兩個類型相同。derived_from
- 指定一個類型是從另一個類型派生的。convertible_to
- 指定一種類型可以隱式轉換為另一種類型。common_with
- 指定兩種類型共用一個公用類型。integral
- 指定型別是整數。default_constructible
- 指定可以預設建構類型的物件。比較概念:
boolean
- 指定可以在布林上下文中使用的類型。equality_comparable
- 指定operator==
是等價關係式。對象概念:
movable
- 指定某種類型的物件可以移動和交換。copyable
- 指定某種類型的物件可以被複製、移動和交換。semiregular
- 指定某種類型的物件可以被複製、移動、交換和預設建構。regular
- 指定類型是正規類型,即它既是semiregular
類型又是equality_comparable
。可呼叫概念:
invocable
- 指定可以使用給定的一組參數類型來呼叫可呼叫類型。predicate
- 指定可呼叫類型是布林謂詞。另請參閱:概念。
將printf
的簡單性與iostream
的型別安全性結合。使用大括號作為佔位符,並支援類似於 printf 樣式說明符的自訂格式。
std::format ( " {1} {0} " , " world " , " hello " ); // == "hello world"
int x = 123 ;
std::string str = std::format( " x: {} " , x); // str == "x: 123"
// Format to an output iterator:
for ( auto x : { 1 , 2 , 3 }) {
std::format_to (std::ostream_iterator< char >{std::cout, " n " }, " {} " , x);
}
若要設定自訂類型的格式:
struct fraction {
int numerator;
int denominator;
};
template <>
struct std ::formatter<fraction>
{
constexpr auto parse (std::format_parse_context& ctx) {
return ctx. begin ();
}
auto format ( const fraction& f, std::format_context& ctx) const {
return std::format_to (ctx. out (), " {0:d}/{1:d} " , f. numerator , f. denominator );
}
};
fraction f{ 1 , 2 };
std::format ( " {} " , f); // == "1/2"
緩衝包裝輸出流的輸出操作,確保同步(即無輸出交錯)。
std::osyncstream{std::cout} << " The value of x is: " << x << std::endl;
跨度是容器的視圖(即非擁有),提供對連續元素組的邊界檢查存取。由於視圖不擁有它們的元素,因此它們的構造和複製成本很低——考慮視圖的一種簡化方法是它們持有對其資料的引用。與維護指標/迭代器和長度欄位相反,跨度將這兩者包裝在一個物件中。
跨度可以是動態大小的或固定大小的(稱為它們的範圍)。固定大小的跨度受益於邊界檢查。
Span 不會傳播 const,因此要構造唯讀 span,請使用std::span<const T>
。
範例:使用動態大小的跨度從各種容器列印整數。
void print_ints (std::span< const int > ints) {
for ( const auto n : ints) {
std::cout << n << std::endl;
}
}
print_ints (std::vector{ 1 , 2 , 3 });
print_ints (std::array< int , 5 >{ 1 , 2 , 3 , 4 , 5 });
int a[ 10 ] = { 0 };
print_ints (a);
// etc.
範例:對於與跨度範圍不符的容器,靜態大小的跨度將無法編譯。
void print_three_ints (std::span< const int , 3 > ints) {
for ( const auto n : ints) {
std::cout << n << std::endl;
}
}
print_three_ints (std::vector{ 1 , 2 , 3 }); // ERROR
print_three_ints (std::array< int , 5 >{ 1 , 2 , 3 , 4 , 5 }); // ERROR
int a[ 10 ] = { 0 };
print_three_ints (a); // ERROR
std::array< int , 3 > b = { 1 , 2 , 3 };
print_three_ints (b); // OK
// You can construct a span manually if required:
std::vector c{ 1 , 2 , 3 };
print_three_ints (std::span< const int , 3 >{ c. data (), 3 }); // OK: set pointer and length field.
print_three_ints (std::span< const int , 3 >{ c. cbegin (), c. cend () }); // OK: use iterator pairs.
C++20 提供了一個新的<bit>
標頭,它提供了一些位元操作,包括 popcount。
std::popcount ( 0u ); // 0
std::popcount ( 1u ); // 1
std::popcount ( 0b1111'0000u ); // 4
<numbers>
頭中定義的數學常數包括PI、歐拉數等。
std::numbers:: pi ; // 3.14159...
std::numbers::e; // 2.71828...
謂詞函數在編譯時上下文中呼叫時為真。
constexpr bool is_compile_time () {
return std::is_constant_evaluated ();
}
constexpr bool a = is_compile_time(); // true
bool b = is_compile_time(); // false
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
和ends_with
成員函數來檢查字串是否以給定字串開頭或結尾。
std::string str = " foobar " ;
str.starts_with( " foo " ); // true
str.ends_with( " baz " ); // false
關聯容器(例如集合和映射)具有contains
成員函數,可以使用它來取代「尋找並檢查迭代器末尾」習慣用法。
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
將物件從一種類型重新解釋為另一種類型的更安全的方法。
float f = 123.0 ;
int i = std::bit_cast< int >(f);
安全地計算兩個整數的中點(不會溢位)。
std::midpoint ( 1 , 3 ); // == 2
將給定的陣列/“類別數組”物件轉換為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>`
將前 N 個參數(其中 N 是std::bind_front
給定函數之後的參數數量)綁定到給定的自由函數、lambda 或成員函數。
const auto f = []( int a, int b, int c) { return a + b + c; };
const auto g = std::bind_front(f, 1 , 1 );
g ( 1 ); // == 3
為各種 STL 容器(例如字串、列表、向量、映射等)提供std::erase
和/或std::erase_if
若要按值擦除,請使用std::erase
,或要指定何時擦除元素的謂詞,請使用std::erase_if
。這兩個函數都傳回已擦除元素的數量。
std::vector v{ 0 , 1 , 0 , 2 , 0 , 3 };
std::erase (v, 0 ); // v == {1, 2, 3}
std::erase_if (v, []( int n) { return n == 0 ; }); // v == {1, 2, 3}
用於比較結果命名的輔助函數:
std::is_eq ( 0 <=> 0 ); // == true
std::is_lteq ( 0 <=> 1 ); // == true
std::is_gt ( 0 <=> 1 ); // == false
另請參閱:三向比較。
使用三向比較按字典順序比較兩個範圍,並產生最強的適用比較類別類型的結果。
std::vector a{ 0 , 0 , 0 }, b{ 0 , 0 , 0 }, c{ 1 , 1 , 1 };
auto cmp_ab = std::lexicographical_compare_three_way(
a.begin(), a.end(), b.begin(), b.end());
std::is_eq (cmp_ab); // == true
auto cmp_ac = std::lexicographical_compare_three_way(
a.begin(), a.end(), c.begin(), c.end());
std::is_lt (cmp_ac); // == true
另請參閱:三向比較、三向比較助手。
自動模板參數推導非常類似於函數的推導方式,但現在包括類別建構函數。
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>
遵循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 >();
* - 例如,您不能使用double
作為模板參數類型,這也使得使用auto
進行無效推導。
折疊表達式透過二元運算子執行模板參數包的折疊。
(... op e)
或(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
推導的變更。以前, 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。
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);
this
在 lambda 環境中捕獲this
以前僅供參考。存在問題的範例是使用回呼的非同步程式碼,該回調要求物件可用(可能超過其生命週期)。 *this
(C++17) 現在將複製當前對象,而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
內聯說明符可以應用於變數以及函數。宣告為內聯的變數與宣告為內聯的函數具有相同的語意。
// 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
它也可以用於聲明和定義靜態成員變量,這樣就不需要在來源文件中對其進行初始化。
struct S {
S () : id{count++} {}
~S () { count--; }
int id;
static inline int count{ 0 }; // declare and initialize count to 0 within the class
};
使用命名空間解析運算子建立嵌套命名空間定義。
namespace A {
namespace B {
namespace C {
int i;
}
}
}
上面的程式碼可以這樣寫:
namespace A ::B::C {
int i;
}
解構初始化的提議,允許編寫auto [ x, y, z ] = expr;
其中expr
的類型是一個類似元組的對象,其元素將綁定到變數x
、 y
和z
(此構造宣告的變數)。類似元組的物件包括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 ());
}
編寫根據編譯時條件實例化的程式碼。
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);
以u8
開頭的字元文字是char
類型的字元文字。 UTF-8 字元文字的值等於其 ISO 10646 代碼點值。
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
C++17 引入了三個新屬性: [[fallthrough]]
、 [[nodiscard]]
和[[maybe_unused]]
。
[[fallthrough]]
向編譯器指示 switch 語句中的失敗是預期行為。此屬性只能在 switch 語句中使用,並且必須放置在下一個 case/default 標籤之前。 switch (n) {
case 1 :
// ...
[[fallthrough]];
case 2 :
// ...
break ;
case 3 :
// ...
[[fallthrough]];
default :
// ...
}
[[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 (operand)
運算子可在#if
和#elif
表達式中使用,以檢查頭檔或原始檔( operand
)是否可用於包含。
一種用例是使用兩個以相同方式工作的庫,如果系統上找不到首選庫,則使用備份/實驗庫。
# 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
它還可用於包含各種平台上不同名稱或位置下存在的標頭,而無需知道程式在哪個平台上運行,OpenGL 標頭就是一個很好的例子,它位於 macOS 上的OpenGL
目錄和其他平台上的GL
中。
# 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
類別模板參數推導(CTAD) 允許編譯器從建構子參數推導模板參數。
std::vector v{ 1 , 2 , 3 }; // deduces std::vector<int>
std::mutex mtx;
auto lck = std::lock_guard{ mtx }; // deduces to std::lock_guard<std::mutex>
auto p = new std::pair{ 1.0 , 2.0 }; // deduces to std::pair<double, double>*
對於使用者定義類型,可以使用推導指南來指導編譯器如何推導模板參數(如果適用):
template < typename T>
struct container {
container (T t) {}
template < typename Iter>
container (Iter beg, Iter end);
};
// deduction guide
template < typename Iter>
container (Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
container a{ 7 }; // OK: deduces container<int>
std::vector< double > v{ 1.0 , 2.0 , 3.0 };
auto b = container{ v. begin (), v. end () }; // OK: deduces container<double>
container c{ 5 , 6 }; // ERROR: std::iterator_traits<int>::value_type is not a type
類別模板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
管理一個可選的包含值,即可能存在也可能不存在的值。可選的一個常見用例是可能失敗的函數的傳回值。
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 x { 5 };
x.has_value() // == true
std::any_cast< int >(x) // == 5
std::any_cast< int &>(x) = 10 ;
std::any_cast< int >(x) // == 10
對字串的非擁有引用。對於在字串之上提供抽像很有用(例如用於解析)。
// 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"
使用參數呼叫Callable
物件。可呼叫物件的範例有std::function
或 lambda;可以像常規函數一樣呼叫的物件。
template < typename Callable>
class Proxy {
Callable c_;
public:
Proxy (Callable c) : c_{ std::move (c) } {}
template < typename ... Args>
decltype ( auto ) operator ()(Args&&... args) {
// ...
return std::invoke (c_, std::forward<Args>(args)...);
}
};
const auto add = []( int x, int y) { return x + y; };
Proxy p{ add };
p ( 1 , 2 ); // == 3
使用參數元組呼叫Callable
物件。
auto add = []( int x, int y) {
return x + y;
};
std::apply (add, std::make_tuple( 1 , 2 )); // == 3
新的std::filesystem
庫提供了操作檔案系統中的檔案、目錄和路徑的標準方法。
此處,如果有可用空間,則將大檔案複製到臨時路徑:
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
類型提供了將資料表示為位元組的標準方法。與char
或unsigned char
相比,使用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
請注意, std::byte
只是一個枚舉,並且由於枚舉的直接列表初始化,枚舉的花括號初始化成為可能。
移動節點和合併容器,無需昂貴的複製、移動或堆疊分配/解除分配的開銷。
將元素從一張地圖移動到另一張地圖:
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" } };
插入一整套:
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 }
插入比容器壽命更長的元素:
auto elementFactory () {
std::set<...> s;
s. emplace (...);
return s. extract (s. begin ());
}
s2.insert(elementFactory());
更改地圖元素的鍵:
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
它們翻譯為「順序」、「平行」和「並行無序」。
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));
對給定序列中的 n 個元素進行取樣(不進行替換),其中每個元素被選擇的機會均等。
const std::string ALLOWED_CHARS = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 " ;
std::string guid;
// Sample 5 characters from ALLOWED_CHARS.
std::sample (ALLOWED_CHARS.begin(), ALLOWED_CHARS.end(), std::back_inserter(guid),
5, std::mt19937{ std::random_device{}() });
std::cout << guid; // e.g. G1fW2
將給定值限制在下限和上限之間。
std::clamp ( 42 , - 1 , 1 ); // == 1
std::clamp (- 42 , - 1 , 1 ); // == -1
std::clamp ( 0 , - 1 , 1 ); // == 0
// `std::clamp` also accepts a custom comparator:
std::clamp ( 0 , - 1 , 1 , std::less<>{}); // == 0
折疊給定範圍的元素。概念上類似於std::accumulate
,但std::reduce
將並行執行折疊。由於折疊是並行完成的,如果指定二元運算,則需要結合和交換。給定的二元運算也不應該更改給定範圍內的任何元素或使任何迭代器無效。
預設的二元運算是 std::plus,初始值為 0。
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::reduce (std::cbegin(a), std::cend(a)); // == 6
// Using a custom binary op:
std::reduce (std::cbegin(a), std::cend(a), 1, std::multiplies<>{}); // == 6
此外,您也可以指定減速器的轉換:
std::transform_reduce (std::cbegin(a), std::cend(a), 0, std::plus<>{}, times_ten); // == 60
const std::array< int , 3 > b{ 1 , 2 , 3 };
const auto product_times_ten = []( const auto a, const auto b) { return a * b * 10 ; };
std::transform_reduce (std::cbegin(a), std::cend(a), std::cbegin(b), 0, std::plus<>{}, product_times_ten); // == 140
支援前綴和(包含和排除掃描)以及轉換。
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}); // 1 3 6
std::exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}); // 0 1 3
const auto times_ten = []( const auto n) { return n * 10 ; };
std::transform_inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}, times_ten); // 10 30 60
std::transform_exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}, times_ten); // 0 10 30
最大公約數 (GCD) 和最小公倍數 (LCM)。
const int p = 9 ;
const int q = 3 ;
std::gcd (p, q); // == 3
std::lcm (p, q); // == 9
傳回給定函數結果的否定的實用函數。
const std::ostream_iterator< int > ostream_it{ std::cout, " " };
const auto is_even = []( const auto n) { return n % 2 == 0 ; };
std::vector< int > v{ 0 , 1 , 2 , 3 , 4 };
// Print all even numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, is_even); // 0 2 4
// Print all odd (not even) numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, std::not_fn(is_even)); // 1 3
將積分和浮點數轉換為字串,反之亦然。轉換是非拋出的、不分配的,並且比 C 標準庫中的等效轉換更安全。
使用者負責分配std::to_chars
所需的足夠儲存空間,否則該函數將因在其傳回值中設定錯誤代碼物件而失敗。
這些函數可讓您選擇傳遞基數(預設為基數 10)或浮點類型輸入的格式說明符。
std::to_chars
傳回一個(非常量)char 指針,該指針是函數寫入給定緩衝區內的字串末尾的一位,以及一個錯誤代碼物件。std::from_chars
傳回一個 const char 指針,成功時等於傳遞給函數的結束指針,以及一個錯誤代碼物件。從這些函數傳回的兩個錯誤代碼物件都等於成功時預設初始化的錯誤代碼物件。
將數字123
轉換為std::string
:
const int n = 123 ;
// Can use any container, string, array, etc.
std::string str;
str.resize( 3 ); // hold enough storage for each digit of `n`
const auto [ ptr, ec ] = std::to_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << str << std::endl; } // 123
else { /* handle failure */ }
從值為"123"
的std::string
轉換為整數:
const std::string str{ " 123 " };
int n;
const auto [ ptr, ec ] = std::from_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << n << std::endl; } // 123
else { /* handle failure */ }
為std::chrono::duration
和std::chrono::time_point
提供abs、round、ceil和floor輔助函式。
using seconds = std::chrono::seconds;
std::chrono::milliseconds d{ 5500 };
std::chrono::abs (d); // == 5s
std::chrono::round<seconds>(d); // == 6s
std::chrono::ceil<seconds>(d); // == 6s
std::chrono::floor<seconds>(d); // == 5s
二進位文字提供了一種表示以 2 為基數的數字的便捷方法。可以用'
分隔數字。
0b110 // == 6
0b1111'1111 // == 255
C++14 現在允許在參數清單中使用auto
類型說明符,從而啟用多態 lambda。
auto identity = []( auto x) { return x; };
int three = identity( 3 ); // == 3
std::string foo = identity( " foo " ); // == "foo"
這允許創建使用任意表達式初始化的 lambda 捕獲。為捕獲的值指定的名稱不需要與封閉範圍中的任何變數相關,並且會在 lambda 主體內引入新名稱。初始化表達式在創建lambda 時進行計算(而不是在呼叫它時)。
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
因為現在可以將值移動(或轉送)到以前只能透過複製或引用捕獲的 lambda 中,所以我們現在可以按值捕獲 lambda 中的僅移動類型。請注意,在下面的範例中, =
左側的task2
捕獲清單中的p
是 lambda 主體私有的新變量,並不引用原始p
。
auto p = std::make_unique< int >( 1 );
auto task1 = [=] { *p = 5 ; }; // ERROR: std::unique_ptr cannot be copied
// 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
使用此引用擷取可以具有與引用變數不同的名稱。
auto x = 1 ;
auto f = [&r = x, x = x * 10 ] {
++r;
return r + x;
};
f (); // sets x to 2 and returns 12
在 C++14 中使用auto
傳回類型,編譯器將嘗試為您推斷類型。借助 lambda,您現在可以使用auto
推導其返回類型,這使得返回推導的引用或右值引用成為可能。
// 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)
型別說明符也可以像auto
一樣推導型別。但是,它會在保留引用和 cv 限定詞的同時推導回傳類型,而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);
另請參見: decltype (C++11)
。
在 C++11 中, constexpr
函數本體只能包含非常有限的語法集,包括(但不限於): typedef
、 using
和單一return
語句。在 C++14 中,允許的語法集大幅擴展,包括最常見的語法,例如if
語句、多個return
、迴圈等。
constexpr int factorial ( int n) {
if (n <= 1 ) {
return 1 ;
} else {
return n * factorial (n - 1 );
}
}
factorial ( 5 ); // == 120
C++14 允許變數模板化:
template < class T >
constexpr T pi = T( 3.1415926535897932385 );
template < class T >
constexpr T e = T( 2.7182818284590452353 );
C++14 引入了[[deprecated]]
屬性來指示不鼓勵使用某個單元(函數、類別等),並且可能會產生編譯警告。如果提供原因,它將包含在警告中。
[[deprecated]]
void old_method ();
[[deprecated( " Use new_method instead " )]]
void legacy_method ();
標準庫類型的新使用者定義文字,包括chrono
和basic_string
的新內建文字。這些可以是constexpr
,這意味著它們可以在編譯時使用。這些文字的一些用途包括編譯時整數解析、二進位文字和虛數文字。
using namespace std ::chrono_literals ;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440
類別模板std::integer_sequence
表示編譯時整數序列。有一些構建在上面的助手:
std::make_integer_sequence<T, N>
- 建立類型為T
的0, ..., N - 1
序列。std::index_sequence_for<T...>
- 將模板參數包轉換為整數序列。將數組轉換為元組:
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::unique_ptr
實例的建議方法:
new
運算符。foo
: foo (std::unique_ptr<T>{ new T{}}, function_that_throws(), std::unique_ptr<T>{ new T{}});
編譯器可以自由地呼叫new T{}
,然後調用function_that_throws()
,依此類推...由於我們在T
的第一次構造中已經在堆上分配了數據,因此我們在這裡引入了洩漏。使用std::make_unique
,我們獲得了異常安全:
foo (std::make_unique<T>(), function_that_throws(), std::make_unique<T>());
有關std::unique_ptr
和std::shared_ptr
的更多信息,請參閱智慧指標 (C++11) 部分。
移動物件意味著將其管理的某些資源的所有權轉移給另一個物件。
移動語意的第一個好處是效能最佳化。當一個物件即將結束其生命週期時,無論是因為它是臨時的還是透過明確調用std::move
,移動通常是一種更便宜的資源轉移方式。例如,移動std::vector
只是將一些指標和內部狀態複製到新向量 - 複製將涉及必須複製向量中包含的每個元素,如果舊向量很快就會被複製,那麼這是昂貴且不必要的被毀了。
移動也使不可複製類型(例如std::unique_ptr
(智慧指標))能夠在語言層級保證一次僅管理一個資源實例,同時能夠傳輸實例範圍之間。
請參閱以下部分:右值參考、移動語意的特殊成員函數、 std::move
、 std::forward
、 forwarding references
。
C++11 引進了一種新的引用,稱為右值引用。對T
右值引用是非範本類型參數(例如int
或使用者定義類型),使用語法T&&
建立。右值引用僅綁定到右值。
使用左值和右值進行類型推導:
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)
另請參閱: std::move
、 std::forward
、 forwarding references
。
也稱為(非官方)通用參考。使用語法T&&
建立轉送引用,其中T
是範本類型參數,或使用auto&&
建立。這實現了完美的轉發:能夠在傳遞參數的同時保持其值類別(例如,左值保留為左值,臨時值作為右值轉發)。
轉發引用允許引用根據類型綁定到左值或右值。轉寄引用遵循引用折疊規則:
T& &
變成T&
T& &&
變成T&
T&& &
變成T&
T&& &&
變成T&&
使用左值和右值進行auto
類型推導:
int x = 0 ; // `x` is an lvalue of type `int`
auto && al = x; // `al` is an lvalue of type `int&` -- binds to the lvalue, `x`
auto && ar = 0 ; // `ar` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
使用左值和右值進行範本類型參數推導:
// Since C++14 or later:
void f ( auto && t) {
// ...
}
// Since C++11 or later:
template < typename T>
void f (T&& t) {
// ...
}
int x = 0 ;
f ( 0 ); // T is int, deduces as f(int &&) => f(int&&)
f (x); // T is int&, deduces as f(int& &&) => f(int&)
int & y = x;
f (y); // T is int&, deduces as f(int& &&) => f(int&)
int && z = 0 ; // NOTE: `z` is an lvalue with type `int&&`.
f (z); // T is int&, deduces as f(int& &&) => f(int&)
f (std::move(z)); // T is int, deduces as f(int &&) => f(int&&)
另請參閱: std::move
、 std::forward
、 rvalue references
。
...
語法建立參數包或擴充參數包。模板參數包是接受零個或多個模板實參(非類型、類型或模板)的模板參數。具有至少一個參數包的模板稱為可變參數模板。
template < typename ... T>
struct arity {
constexpr static int value = sizeof ...(T);
};
static_assert (arity<>::value == 0 );
static_assert (arity< char , short , int >::value == 3 );
一個有趣的用途是從參數包建立一個初始化列表,以便迭代可變參數函數參數。
template < typename First, typename ... Args>
auto sum ( const First first, const Args... args) -> decltype(first) {
const auto values = {first, args...};
return std::accumulate (values. begin (), values. end (), First{ 0 });
}
sum ( 1 , 2 , 3 , 4 , 5 ); // 15
sum ( 1 , 2 , 3 ); // 6
sum ( 1.5 , 2.0 , 3.7 ); // 7.2
使用「花括號清單」語法建立的輕量級類似陣列的元素容器。例如, { 1, 2, 3 }
建立一個整數序列,其類型為std::initializer_list<int>
。用作將物件向量傳遞給函數的替代方法。
int sum ( const std::initializer_list< int >& list) {
int total = 0 ;
for ( auto & e : list) {
total += e;
}
return total;
}
auto list = { 1 , 2 , 3 };
sum (list); // == 6
sum ({ 1 , 2 , 3 }); // == 6
sum ({}); // == 0
在編譯時評估的斷言。
constexpr int x = 0 ;
constexpr int y = 1 ;
static_assert (x == y, " x != y " );
auto
類型變數由編譯器根據其初始值設定項目的類型推導。
auto a = 3.14 ; // double
auto b = 1 ; // int
auto & c = b; // int&
auto d = { 0 }; // std::initializer_list<int>
auto && e = 1 ; // int&&
auto && f = b; // int&
auto g = new auto ( 123 ); // int*
const auto h = 1 ; // const int
auto i = 1 , j = 2 , k = 3 ; // int, int, int
auto l = 1 , m = true , n = 1.61 ; // error -- `l` deduced to be int, `m` is bool
auto o; // error -- `o` requires initializer
對於可讀性非常有用,特別是對於複雜的類型:
std::vector< int > v = ...;
std::vector< int >::const_iterator cit = v.cbegin();
// vs.
auto cit = v.cbegin();
函數也可以使用auto
推導回傳類型。在 C++11 中,必須明確指定傳回類型,或使用decltype
指定,如下所示:
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2 ); // == 3
add ( 1 , 2.0 ); // == 3.0
add ( 1.5 , 1.5 ); // == 3.0
上面範例中的尾隨回傳類型是表達式x + y
的宣告類型(請參閱有關decltype
的部分)。例如,如果x
是整數且y
是雙精確度型, decltype(x + y)
是雙精確型。因此,上面的函數將根據表達式x + y
產生的類型來推斷類型。請注意,尾隨返回類型可以存取其參數,並且在適當的情況下可以this
。
lambda
是一個未命名的函數對象,能夠捕捉作用域中的變數。它的特點是:捕獲清單;一組可選的參數,帶有可選的尾隨返回類型;和一個身體。捕獲清單範例:
[]
- 沒有捕獲任何內容。[=]
- 按值擷取範圍內的局部物件(局部變數、參數)。[&]
- 透過引用捕捉作用域中的局部物件(局部變數、參數)。[this]
- 透過引用捕捉this
。[a, &b]
- 透過值捕捉物件a
,透過引用捕捉b
。 int x = 1 ;
auto getX = [=] { return x; };
getX (); // == 1
auto addX = [=]( int y) { return x + y; };
addX ( 1 ); // == 2
auto getXRef = [&]() -> int & { return x; };
getXRef (); // int& to `x`
預設情況下,無法在 lambda 內部修改值捕獲,因為編譯器產生的方法被標記為const
。 mutable
關鍵字允許修改捕獲的變數。關鍵字放置在參數清單之後(即使參數清單為空,也必須存在)。
int x = 1 ;
auto f1 = [&x] { x = 2 ; }; // OK: x is a reference and modifies the original
auto f2 = [x] { x = 2 ; }; // ERROR: the lambda can only perform const-operations on the captured value
// vs.
auto f3 = [x]() mutable { x = 2 ; }; // OK: the lambda can perform any operations on the captured value
decltype
是一個運算符,它傳回傳遞給它的表達式的宣告類型。如果 cv 限定詞和參考是表達式的一部分,則它們會被保留。 decltype
的範例:
int a = 1 ; // `a` is declared as type `int`
decltype (a) b = a; // `decltype(a)` is `int`
const int & c = a; // `c` is declared as type `const int&`
decltype (c) d = a; // `decltype(c)` is `const int&`
decltype ( 123 ) e = 123; // `decltype(123)` is `int`
int && f = 1 ; // `f` is declared as type `int&&`
decltype (f) g = 1; // `decltype(f) is `int&&`
decltype ((a)) h = g; // `decltype((a))` is int&
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2.0 ); // `decltype(x + y)` => `decltype(3.0)` => `double`
另請參閱: decltype(auto) (C++14)
。
然而,在語義上與使用typedef
類似,使用using
類型別名更易於閱讀並且與模板相容。
template < typename T>
using Vec = std::vector<T>;
Vec< int > v; // std::vector<int>
using String = std::string;
String s { " foo " };
C++11 引進了一種新的空指標類型,旨在取代 C 的NULL
巨集。 nullptr
本身是std::nullptr_t
類型,可以隱式轉換為指標類型,並且與NULL
不同,不能轉換為除bool
之外的整數類型。
void foo ( int );
void foo ( char *);
foo ( NULL ); // error -- ambiguous
foo ( nullptr ); // calls foo(char*)
類型安全枚舉解決了 C 風格枚舉的各種問題,包括:隱式轉換、無法指定基礎類型、範圍污染。
// Specifying underlying type as `unsigned int`
enum class Color : unsigned int { Red = 0xff0000 , Green = 0xff00 , Blue = 0xff };
// `Red`/`Green` in `Alert` don't conflict with `Color`
enum class Alert : bool { Red, Green };
Color c = Color::Red;
屬性提供了__attribute__(...)
、 __declspec
等的通用語法。
// `noreturn` attribute indicates `f` doesn't return.
[[ noreturn ]] void f () {
throw " error " ;
}
常數表達式是編譯器在編譯時可能計算的表達式。常量表達式中只能進行非複雜的計算(這些規則在以後的版本中逐漸放寬)。使用constexpr
說明符來指示變數、函數等是常數表達式。
constexpr int square ( int x) {
return x * x;
}
int square2 ( int x) {
return x * x;
}
int a = square( 2 ); // mov DWORD PTR [rbp-4], 4
int b = square2( 2 ); // mov edi, 2
// call square2(int)
// mov DWORD PTR [rbp-8], eax
在前面的程式碼片段中,請注意,呼叫square
時的計算是在編譯時進行的,然後結果嵌入到程式碼產生中,而square2
是在執行時呼叫的。
constexpr
值是編譯器可以在編譯時計算但不能保證的值:
const int x = 123 ;
constexpr const int & y = x; // error -- constexpr variable `y` must be initialized by a constant expression
帶有類別的常數表達式:
struct Complex {
constexpr Complex ( double r, double i) : re{r}, im{i} { }
constexpr double real () { return re; }
constexpr double imag () { return im; }
private:
double re;
double im;
};
constexpr Complex I ( 0 , 1 );
建構子現在可以使用初始值設定項清單呼叫同一類別中的其他建構子。
struct Foo {
int foo;
Foo ( int foo) : foo{foo} {}
Foo () : Foo( 0 ) {}
};
Foo foo;
foo.foo; // == 0
使用者定義的文字可讓您擴展語言並添加您自己的語法。若要建立文字,請定義一個T operator "" X(...) { ... }
函數,該函數傳回類型T
,名稱為X
。請注意,該函數的名稱定義了文字的名稱。任何不以下劃線開頭的文字名稱都會被保留,不會被呼叫。根據呼叫文字的類型,使用者定義的文字函數應接受哪些參數有一些規則。
將攝氏度轉換為華氏:
// `unsigned long long` parameter required for integer literal.
long long operator " " _celsius( unsigned long long tempCelsius) {
return std::llround (tempCelsius * 1.8 + 32 );
}
24_celsius; // == 75
字串到整數的轉換:
// `const char*` and `std::size_t` required as parameters.
int operator " " _int( const char * str, std:: size_t ) {
return std::stoi (str);
}
" 123 " _int; // == 123, with type `int`
指定一個虛擬函數覆蓋另一個虛擬函數。如果虛擬函數沒有覆寫父級的虛擬函數,則會引發編譯器錯誤。
struct A {
virtual void foo ();
void bar ();
};
struct B : A {
void foo () override ; // correct -- B::foo overrides A::foo
void bar () override ; // error -- A::bar is not virtual
void baz () override ; // error -- B::baz does not override A::baz
};
指定不能在衍生類別中重寫虛函數或不能繼承類別。
struct A {
virtual void foo ();
};
struct B : A {
virtual void foo () final ;
};
struct C : B {
virtual void foo (); // error -- declaration of 'foo' overrides a 'final' function
};
類別不能被繼承。
struct A final {};
struct B : A {}; // error -- base 'A' is marked 'final'
一種更優雅、更有效的方式來提供函數(例如建構函數)的預設實作。
struct A {
A () = default ;
A ( int x) : x{x} {}
int x { 1 };
};
A a; // a.x == 1
A a2 { 123 }; // a.x == 123
具有繼承性:
struct B {
B () : x{ 1 } {}
int x;
};
struct C : B {
// Calls B::B
C () = default ;
};
C c; // c.x == 1
一種更優雅、更有效的方式來提供函數的刪除實作。對於防止物件上的複製很有用。
class A {
int x;
public:
A ( int x) : x{x} {};
A ( const A&) = delete ;
A& operator =( const A&) = delete ;
};
A x { 123 };
A y = x; // error -- call to deleted copy constructor
y = x; // error -- operator= deleted
用於迭代容器元素的語法糖。
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int & x : a) x *= 2 ;
// a == { 2, 4, 6, 8, 10 }
請注意使用int
與int&
時的差異:
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int x : a) x *= 2 ;
// a == { 1, 2, 3, 4, 5 }
複製建構函式和複製賦值運算子在進行複製時被調用,隨著 C++11 引入移動語義,現在有了用於移動的移動建構函式和移動賦值運算子。
struct A {
std::string s;
A () : s{ " test " } {}
A ( const A& o) : s{o. s } {}
A (A&& o) : s{ std::move (o. s )} {}
A& operator =(A&& o) {
s = std::move (o. s );
return * this ;
}
};
A f (A a) {
return a;
}
A a1 = f(A{}); // move-constructed from rvalue temporary
A a2 = std::move(a1); // move-constructed using std::move
A a3 = A{};
a2 = std::move(a3); // move-assignment using std::move
a1 = f(A{}); // move-assignment from rvalue temporary
轉換建構子會將花括號列表語法的值轉換為建構子參數。
struct A {
A ( int ) {}
A ( int , int ) {}
A ( int , int , int ) {}
};
A a { 0 , 0 }; // calls A::A(int, int)
A b ( 0 , 0 ); // calls A::A(int, int)
A c = { 0 , 0 }; // calls A::A(int, int)
A d { 0 , 0 , 0 }; // calls A::A(int, int, int)
請注意,花括號列表語法不允許縮小範圍:
struct A {
A ( int ) {}
};
A a ( 1.1 ); // OK
A b { 1.1 }; // Error narrowing conversion from double to int
請注意,如果建構函式接受std::initializer_list
,則會呼叫它:
struct A {
A ( int ) {}
A ( int , int ) {}
A ( int , int , int ) {}
A (std::initializer_list< int >) {}
};
A a { 0 , 0 }; // calls A::A(std::initializer_list<int>)
A b ( 0 , 0 ); // calls A::A(int, int)
A c = { 0 , 0 }; // calls A::A(std::initializer_list<int>)
A d { 0 , 0 , 0 }; // calls A::A(std::initializer_list<int>)
現在可以使用explicit
說明符使轉換函數明確化。
struct A {
operator bool () const { return true ; }
};
struct B {
explicit operator bool () const { return true ; }
};
A a;
if (a); // OK calls A::operator bool()
bool ba = a; // OK copy-initialization selects A::operator bool()
B b;
if (b); // OK calls B::operator bool()
bool bb = b; // error copy-initialization does not consider B::operator bool()
內聯命名空間的所有成員都被視為其父命名空間的一部分,從而允許函數專門化並簡化版本控制過程。這是一個傳遞屬性,如果 A 包含 B,B 又包含 C,且 B 和 C 都是內聯命名空間,則 C 的成員可以像在 A 上一樣使用。
namespace Program {
namespace Version1 {
int getVersion () { return 1 ; }
bool isFirstVersion () { return true ; }
}
inline namespace Version2 {
int getVersion () { return 2 ; }
}
}
int version { Program::getVersion ()}; // Uses getVersion() from Version2
int oldVersion { Program::Version1::getVersion ()}; // Uses getVersion() from Version1
bool firstVersion { Program::isFirstVersion ()}; // Does not compile when Version2 is added
允許非靜態資料成員在宣告的地方進行初始化,可能會清除預設初始化的建構子。
// Default initialization prior to C++11
class Human {
Human () : age{ 0 } {}
private:
unsigned age;
};
// Default initialization on C++11
class Human {
private:
unsigned age { 0 };
};
C++11 現在能夠推斷何時將一系列直尖括號用作運算子或用作 typedef 的結束語句,而無需添加空格。
typedef std::map< int , std::map < int , std::map < int , int > > > cpp98LongTypedef;
typedef std::map< int , std::map < int , std::map < int , int >>> cpp11LongTypedef;
現在可以根據*this
是左值參考還是右值參考來限定成員函數。
struct Bar {
// ...
};
struct Foo {
Bar& getBar () & { return bar; }
const Bar& getBar () const & { return bar; }
Bar&& getBar() && { return std::move (bar); }
const Bar&& getBar() const && { return std::move (bar); }
private:
Bar bar;
};
Foo foo{};
Bar bar = foo.getBar(); // calls `Bar& getBar() &`
const Foo foo2{};
Bar bar2 = foo2.getBar(); // calls `Bar& Foo::getBar() const&`
Foo{}.getBar(); // calls `Bar&& Foo::getBar() &&`
std::move (foo).getBar(); // calls `Bar&& Foo::getBar() &&`
std::move (foo2).getBar(); // calls `const Bar&& Foo::getBar() const&`
C++11 允許函數和 lambda 使用替代語法來指定其傳回類型。
int f () {
return 123 ;
}
// vs.
auto f () -> int {
return 123 ;
}
auto g = []() -> int {
return 123 ;
};
當某些返回類型無法解析時,此功能特別有用:
// NOTE: This does not compile!
template < typename T, typename U>
decltype (a + b) add(T a, U b) {
return a + b;
}
// Trailing return types allows this:
template < typename T, typename U>
auto add (T a, U b) -> decltype(a + b) {
return a + b;
}
在 C++14 中,可以改用decltype(auto) (C++14)
。
noexcept
說明符指定函數是否可以拋出異常。它是throw()
的改進版本。
void func1 () noexcept ; // does not throw
void func2 () noexcept ( true ); // does not throw
void func3 () throw(); // does not throw
void func4 () noexcept ( false ); // may throw
允許非拋出函數呼叫可能拋出的函數。每當拋出異常且處理程序的搜尋遇到非拋出函數的最外層區塊時,就會呼叫函數 std::terminate。
extern void f (); // potentially-throwing
void g () noexcept {
f (); // valid, even if f throws
throw 42 ; // valid, effectively a call to std::terminate
}
提供用於表示 UTF-8 字串的標準類型。
char32_t utf8_str[] = U" u0123 " ;
char16_t utf8_str[] = u" u0123 " ;
C++11 引入了一種將字串文字宣告為「原始字串文字」的新方法。從轉義序列(製表符、換行符、單反斜線等)發出的字元可以原始輸入,同時保留格式。例如,這對於編寫可能包含大量引用或特殊格式的文學文本很有用。這可以使您的字串文字更易於閱讀和維護。
使用以下語法聲明原始字串文字:
R"delimiter(raw_characters)delimiter"
在哪裡:
delimiter
是由括號、反斜線和空格之外的任何原始字元組成的可選字元序列。raw_characters
是任何原始字元序列;不得包含結束序列")delimiter"
。例子:
// msg1 and msg2 are equivalent.
const char * msg1 = " n Hello, nt world! n " ;
const char * msg2 = R"(
Hello,
world!
)" ;
std::move
表示傳遞給它的物件可能會轉移其資源。使用已移出的物件時應小心使用,因為它們可能會處於未指定的狀態(請參閱:我可以對移出的物件做什麼?)。
std::move
的定義(執行移動只不過是轉換為右值參考):
template < typename T>
typename remove_reference<T>::type&& move(T&& arg) {
return static_cast < typename remove_reference<T>::type&&>(arg);
}
傳輸std::unique_ptr
:
std::unique_ptr< int > p1 { new int { 0 }}; // in practice, use std::make_unique
std::unique_ptr< int > p2 = p1; // error -- cannot copy unique pointers
std::unique_ptr< int > p3 = std::move(p1); // move `p1` into `p3`
// now unsafe to dereference object held by `p1`
傳回傳遞給它的參數,同時保留它們的值類別和 cv 限定詞。對於通用代碼和工廠很有用。與forwarding references
結合使用。
std::forward
的定義:
template < typename T>
T&& forward( typename remove_reference<T>::type& arg) {
return static_cast <T&&>(arg);
}
函數wrapper
的範例,它僅將其他A
物件轉發到新A
物件的複製或移動建構函數:
struct A {
A () = default ;
A ( const A& o) { std::cout << " copied " << std::endl; }
A (A&& o) { std::cout << " moved " << std::endl; }
};
template < typename T>
A wrapper (T&& arg) {
return A{std::forward<T>(arg)};
}
wrapper (A{}); // moved
A a;
wrapper (a); // copied
wrapper (std::move(a)); // moved
另請參閱: forwarding references
、 rvalue references
。
std::thread
函式庫提供了控制執行緒的標準方法,例如產生和終止執行緒。在下面的範例中,產生多個執行緒來執行不同的計算,然後程式等待所有執行緒完成。
void foo ( bool clause) { /* do something... */ }
std::vector<std::thread> threadsVector;
threadsVector.emplace_back([]() {
// Lambda function that will be invoked
});
threadsVector.emplace_back(foo, true ); // thread will run foo(true)
for ( auto & thread : threadsVector) {
thread. join (); // Wait for threads to finish
}
將數字參數轉換為std::string
。
std::to_string ( 1.2 ); // == "1.2"
std::to_string ( 123 ); // == "123"
類型特徵定義了一個基於編譯時模板的介面來查詢或修改類型的屬性。
static_assert (std::is_integral< int >::value);
static_assert (std::is_same< int , int >::value);
static_assert (std::is_same<std::conditional< true , int , double >::type, int >::value);
C++11 引進了新的智慧指標: std::unique_ptr
、 std::shared_ptr
、 std::weak_ptr
。 std::auto_ptr
現在已被棄用,並最終在 C++17 中被刪除。
std::unique_ptr
是一個不可複製、可移動的指針,用來管理自己的堆分配記憶體。注意:偏好使用std::make_X
輔助函數而不是使用建構子。請參閱 std::make_unique 和 std::make_shared 部分。
std::unique_ptr<Foo> p1 { new Foo{} }; // `p1` owns `Foo`
if (p1) {
p1-> bar ();
}
{
std::unique_ptr<Foo> p2 { std::move (p1)}; // Now `p2` owns `Foo`
f (*p2);
p1 = std::move (p2); // Ownership returns to `p1` -- `p2` gets destroyed
}
if (p1) {
p1-> bar ();
}
// `Foo` instance is destroyed when `p1` goes out of scope
std::shared_ptr
是一個智慧指針,用於管理在多個擁有者之間共享的資源。共用指標保存一個控制塊,該控制塊具有一些元件,例如託管物件和引用計數器。所有控制塊存取都是線程安全的,但是,操作託管物件本身不是線程安全的。
void foo (std::shared_ptr<T> t) {
// Do something with `t`...
}
void bar (std::shared_ptr<T> t) {
// Do something with `t`...
}
void baz (std::shared_ptr<T> t) {
// Do something with `t`...
}
std::shared_ptr<T> p1 { new T{}};
// Perhaps these take place in another threads?
foo (p1);
bar (p1);
baz (p1);
chrono 函式庫包含一組處理持續時間、時鐘和時間點的實用函數和型別。該庫的一個用例是對程式碼進行基準測試:
std::chrono::time_point<std::chrono::steady_clock> start, end;
start = std::chrono::steady_clock::now();
// Some computations...
end = std::chrono::steady_clock::now();
std::chrono::duration< double > elapsed_seconds = end - start;
double t = elapsed_seconds.count(); // t number of seconds, represented as a `double`
元組是固定大小的異構值的集合。透過使用std::tie
或使用std::get
解包來存取std::tuple
的元素。
// `playerProfile` has type `std::tuple<int, const char*, const char*>`.
auto playerProfile = std::make_tuple( 51 , " Frans Nielsen " , " NYI " );
std::get< 0 >(playerProfile); // 51
std::get< 1 >(playerProfile); // "Frans Nielsen"
std::get< 2 >(playerProfile); // "NYI"
建立左值引用的元組。對於解壓縮std::pair
和std::tuple
物件很有用。使用std::ignore
作為被忽略值的佔位符。在 C++17 中,應改用結構化綁定。
// With tuples...
std::string playerName;
std::tie (std::ignore, playerName, std::ignore) = std::make_tuple( 91 , " John Tavares " , " NYI " );
// With pairs...
std::string yes, no;
std::tie (yes, no) = std::make_pair( " yes " , " no " );
std::array
是建構在 C 樣式陣列之上的容器。支援排序等常見容器操作。
std::array< int , 3 > a = { 2 , 1 , 3 };
std::sort (a.begin(), a.end()); // a == { 1, 2, 3 }
for ( int & x : a) x *= 2 ; // a == { 2, 4, 6 }
這些容器保持搜尋、插入和刪除操作的平均恆定時間複雜度。為了實現恆定時間複雜度,透過將元素散列到桶中來犧牲順序以換取速度。有四個無序容器:
unordered_set
unordered_multiset
unordered_map
unordered_multimap
由於以下原因, std::make_shared
是建立std::shared_ptr
實例的建議方法:
new
運算符。foo
: foo (std::shared_ptr<T>{ new T{}}, function_that_throws(), std::shared_ptr<T>{ new T{}});
編譯器可以自由地呼叫new T{}
,然後調用function_that_throws()
,依此類推...由於我們在T
的第一次構造中已經在堆上分配了數據,因此我們在這裡引入了洩漏。使用std::make_shared
,我們獲得了異常安全:
foo (std::make_shared<T>(), function_that_throws(), std::make_shared<T>());
std::shared_ptr{ new T{} }
時,我們必須為T
分配內存,然後在共享指標中我們必須為指標內的控制塊分配記憶體。有關std::unique_ptr
和std::shared_ptr
的更多信息,請參閱智慧指標部分。
std::ref(val)
用於建立保存 val 引用的std::reference_wrapper
類型的物件。用於通常使用&
進行引用傳遞無法編譯或&
由於類型推導而被刪除的情況。 std::cref
類似,但建立的引用包裝器保存對 val 的 const 引用。
// create a container to store reference of objects.
auto val = 99 ;
auto _ref = std::ref(val);
_ref++;
auto _cref = std::cref(val);
// _cref++; does not compile
std::vector<std::reference_wrapper< int >>vec; // vector<int&>vec does not compile
vec.push_back(_ref); // vec.push_back(&i) does not compile
cout << val << endl; // prints 100
cout << vec[ 0 ] << endl; // prints 100
cout << _cref; // prints 100
C++11 引入了 C++ 的記憶體模型,這意味著函式庫支援執行緒和原子操作。其中一些操作包括(但不限於)原子載入/儲存、比較和交換、原子標誌、承諾、未來、鎖定和條件變數。
請參閱以下部分: std::thread
std::async
非同步或延遲計算地運行給定函數,然後傳回保存該函數呼叫結果的std::future
。
第一個參數是策略,可以是:
std::launch::async | std::launch::deferred
是否執行非同步執行或延遲計算取決於實作。std::launch::async
在新執行緒上執行可呼叫物件。std::launch::deferred
對目前執行緒執行延遲計算。 int foo () {
/* Do something here, then return the result. */
return 1000 ;
}
auto handle = std::async(std::launch::async, foo); // create an async task
auto result = handle.get(); // wait for the result
新增了std::begin
和std::end
自由函數以一般傳回容器的開始和結束迭代器。這些函數也適用於沒有begin
和end
成員函數的原始陣列。
template < typename T>
int CountTwos ( const T& container) {
return std::count_if ( std::begin (container), std::end (container), []( int item) {
return item == 2 ;
});
}
std::vector< int > vec = { 2 , 2 , 43 , 435 , 4543 , 534 };
int arr[ 8 ] = { 2 , 43 , 45 , 435 , 32 , 32 , 32 , 32 };
auto a = CountTwos(vec); // 2
auto b = CountTwos(arr); // 1
安東尼·卡蘭德拉
請參閱:https://github.com/AnthonyCalandra/modern-cpp-features/graphs/contributors
麻省理工學院