C++20 มีฟีเจอร์ภาษาใหม่ดังต่อไปนี้:
C++20 มีคุณสมบัติไลบรารีใหม่ดังต่อไปนี้:
C++17 มีฟีเจอร์ภาษาใหม่ดังต่อไปนี้:
C++17 มีคุณสมบัติไลบรารีใหม่ดังต่อไปนี้:
C++14 มีฟีเจอร์ภาษาใหม่ดังต่อไปนี้:
C++14 มีคุณสมบัติไลบรารีใหม่ดังต่อไปนี้:
C++11 มีฟีเจอร์ภาษาใหม่ดังต่อไปนี้:
C++11 มีคุณสมบัติไลบรารีใหม่ดังต่อไปนี้:
หมายเหตุ: แม้ว่าตัวอย่างเหล่านี้จะแสดงวิธีใช้ coroutines ในระดับพื้นฐาน แต่ก็ยังมีอะไรอีกมากมายเกิดขึ้นเมื่อมีการคอมไพล์โค้ด ตัวอย่างเหล่านี้ไม่ได้หมายถึงการครอบคลุมคอร์รูทีนของ C++20 อย่างสมบูรณ์ เนื่องจากไลบรารีมาตรฐานยังไม่มี
generator
และคลาสtask
ฉันจึงใช้ไลบรารี cppcoro เพื่อรวบรวมตัวอย่างเหล่านี้
Coroutines เป็นฟังก์ชันพิเศษที่สามารถระงับการดำเนินการและดำเนินการต่อได้ ในการกำหนด coroutine ต้องมีคีย์เวิร์ด co_return
, co_await
หรือ co_yield
อยู่ในเนื้อหาของฟังก์ชัน coroutines ของ 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 loop) co_yield
รับนิพจน์ที่กำหนด ให้ผลตอบแทน (เช่น ส่งคืน) ค่าของมัน และระงับ coroutine ที่จุดนั้น เมื่อดำเนินการต่อ การดำเนินการจะดำเนินต่อไปหลังจาก co_yield
อีกตัวอย่างหนึ่งของ Coroutine คือ งาน ซึ่งเป็นการคำนวณแบบอะซิงโครนัสที่ดำเนินการเมื่อรองานอยู่:
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 Boolean ข้อจำกัด ควรจำลองข้อกำหนดด้านความหมาย เช่น ประเภทเป็นตัวเลขหรือแบบแฮชได้ ข้อผิดพลาดของคอมไพลเลอร์จะเกิดขึ้นหากประเภทที่กำหนดไม่เป็นไปตามแนวคิดที่ผูกไว้ (เช่น 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
ใช้ไวยากรณ์เทมเพลตที่คุ้นเคยในนิพจน์แลมบ์ดา
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
// ...
}
การจับ 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 เข้าสู่ขอบเขตเพื่อปรับปรุงความสามารถในการอ่าน ก่อน:
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.
ช่วยสนับสนุนแมโคร variadic โดยการประเมินอาร์กิวเมนต์ที่กำหนดหากแมโคร variadic ไม่ว่างเปล่า
# 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
- ระบุว่าประเภทเป็น แบบ Regular กล่าวคือ เป็นทั้ง semiregular
และ equality_comparable
แนวคิดที่สามารถเรียกได้:
invocable
- ระบุว่าประเภท callable สามารถเรียกใช้ด้วยชุดประเภทอาร์กิวเมนต์ที่กำหนด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 ดังนั้นเพื่อสร้างการใช้งานช่วงแบบอ่านอย่างเดียว 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
ค่าคงที่ทางคณิตศาสตร์ รวมถึง PI, ตัวเลขของออยเลอร์ ฯลฯ ที่กำหนดไว้ในส่วนหัว <numbers>
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
แปลงวัตถุ array/"array-like" ที่กำหนดให้เป็น 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
) กับฟังก์ชันอิสระ แลมบ์ดา หรือฟังก์ชันสมาชิกที่กำหนด
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
จัดให้มี std::erase
และ/หรือ std::erase_if
สำหรับคอนเทนเนอร์ STL ที่หลากหลาย เช่น สตริง รายการ เวกเตอร์ แผนที่ ฯลฯ
สำหรับการลบตามค่า ให้ใช้ 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
เป็นแพ็กพารามิเตอร์ที่ขยายไม่ได้ เรียกว่า unary folds(e1 op ... op e2)
โดยที่ op
เป็นตัวดำเนินการพับ เรียกว่า binary fold 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
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
ตามมูลค่า ก่อนหน้านี้การบันทึก 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 ได้โดยใช้ไวยากรณ์ที่มีเครื่องหมายปีกกา
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 label ถัดไป 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 เป็นตัวอย่างที่ดีสำหรับสิ่งนี้ซึ่งอยู่ในไดเร็กทอรี OpenGL
บน macOS และ 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
หรือ lambdas วัตถุที่สามารถเรียกได้คล้ายกับฟังก์ชันปกติ
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
ใหม่เป็นวิธีมาตรฐานในการแสดงข้อมูลในรูปแบบไบต์ ประโยชน์ของการใช้ std::byte
บน char
หรือ unsigned char
ก็คือ ไม่ใช่ประเภทอักขระ และไม่ใช่ประเภทเลขคณิตด้วย ในขณะที่โอเปอเรเตอร์โอเวอร์โหลดเพียงตัวเดียวเท่านั้นที่เป็นการดำเนินการระดับบิต
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
เป็นเพียง enum และการกำหนดค่าเริ่มต้นของ enum แบบวงเล็บปีกกาก็เป็นไปได้ด้วยการกำหนดค่าเริ่มต้นรายการโดยตรงของ enum
การย้ายโหนดและการรวมคอนเทนเนอร์โดยไม่ต้องเสียค่าใช้จ่ายในการคัดลอก การย้าย หรือการจัดสรรฮีป/การจัดสรรคืนที่มีราคาแพง
การย้ายองค์ประกอบจากแผนที่หนึ่งไปยังอีกแผนที่หนึ่ง:
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
ส่งคืนตัวชี้อักขระ (ไม่ใช่ const) ซึ่งเป็นจุดสิ้นสุดหนึ่งของสตริงที่ฟังก์ชันเขียนถึงภายในบัฟเฟอร์ที่กำหนด และวัตถุรหัสข้อผิดพลาด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 */ }
แปลงจาก std::string
ที่มีค่า "123"
เป็นจำนวนเต็ม:
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 */ }
จัดเตรียมฟังก์ชันตัวช่วย abs, round, ceil และ floor สำหรับ std::chrono::duration
และ std::chrono::time_point
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
ในรายการพารามิเตอร์ ซึ่งเปิดใช้งาน lambdas แบบ polymorphic
auto identity = []( auto x) { return x; };
int three = identity( 3 ); // == 3
std::string foo = identity( " foo " ); // == "foo"
ซึ่งช่วยให้สามารถสร้างการจับภาพแลมบ์ดาที่เริ่มต้นด้วยนิพจน์ที่กำหนดเองได้ ชื่อที่กำหนดให้กับค่าที่บันทึกไว้ไม่จำเป็นต้องเกี่ยวข้องกับตัวแปรใดๆ ในขอบเขตที่ล้อมรอบ และแนะนำชื่อใหม่ภายในเนื้อความแลมบ์ดา นิพจน์การเริ่มต้นจะถูกประเมินเมื่อมี การสร้าง แลมบ์ดา (ไม่ใช่เมื่อมี การเรียกใช้ )
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
เนื่องจากขณะนี้มีความเป็นไปได้ที่จะ ย้าย (หรือ ส่งต่อ ) ค่าไปยังแลมบ์ดาซึ่งก่อนหน้านี้สามารถจับได้โดยการคัดลอกหรือการอ้างอิงเท่านั้น เราจึงสามารถจับประเภทเฉพาะการย้ายในแลมบ์ดาตามค่าได้ โปรดทราบว่าในตัวอย่างด้านล่าง p
ในรายการดักจับของ task2
ทางด้านซ้ายของ =
เป็นตัวแปรใหม่ส่วนตัวสำหรับเนื้อแลมบ์ดาและไม่ได้อ้างอิงถึง 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
การใช้ประเภทการส่งคืน auto
ใน C ++ 14 คอมไพเลอร์จะพยายามอนุมานประเภทให้คุณ ด้วย lambdas ตอนนี้คุณสามารถอนุมานประเภทการส่งคืนได้โดยใช้ auto
ซึ่งทำให้การส่งคืนการอ้างอิงแบบอนุมานหรือการอ้างอิงค่า r เป็นไปได้
// 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
s using
s และคำสั่ง return
เดียว ใน C++14 ชุดของไวยากรณ์ที่อนุญาตจะขยายออกไปอย่างมากเพื่อรวมไวยากรณ์ที่พบบ่อยที่สุด เช่น คำสั่ง if
, multiple return
s, ลูป ฯลฯ
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>
- สร้างลำดับของ 0, ..., N - 1
ด้วยประเภท T
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
s เนื่องจากเหตุผลต่อไปนี้:
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>());
ดูส่วนของตัวชี้อัจฉริยะ (C ++ 11) สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ std::unique_ptr
และ std::shared_ptr
การย้ายวัตถุหมายถึงการโอนความเป็นเจ้าของทรัพยากรบางส่วนที่วัตถุจัดการไปยังวัตถุอื่น
ประโยชน์ประการแรกของอรรถศาสตร์การย้ายคือการเพิ่มประสิทธิภาพการทำงาน เมื่ออ็อบเจ็กต์กำลังจะหมดอายุการใช้งาน เนื่องจากเป็นการชั่วคราวหรือโดยการเรียก std::move
อย่างชัดเจน การย้ายมักจะเป็นวิธีที่ถูกกว่าในการถ่ายโอนทรัพยากร ตัวอย่างเช่น การย้าย std::vector
เป็นเพียงการคัดลอกพอยน์เตอร์และสถานะภายในไปยังเวกเตอร์ใหม่ การคัดลอกเกี่ยวข้องกับการคัดลอกทุกองค์ประกอบที่มีอยู่ในเวกเตอร์ ซึ่งมีราคาแพงและไม่จำเป็นหากเวกเตอร์เก่าจะเร็ว ๆ นี้ ถูกทำลาย
การย้ายยังช่วยให้ประเภทที่ไม่สามารถคัดลอกได้ เช่น std::unique_ptr
s (ตัวชี้อัจฉริยะ) รับประกันในระดับภาษาว่าจะมีการจัดการทรัพยากรเพียงอินสแตนซ์เดียวเท่านั้นในแต่ละครั้ง ในขณะที่สามารถถ่ายโอนอินสแตนซ์ได้ ระหว่างขอบเขต
ดูหัวข้อต่างๆ เกี่ยวกับ: การอ้างอิงค่า ฟังก์ชันสมาชิกพิเศษสำหรับซีแมนทิกส์การย้าย std::move
, std::forward
forwarding references
C ++ 11 แนะนำการอ้างอิงใหม่ที่เรียกว่า การอ้างอิง rvalue การอ้างอิงค่า r ถึง T
ซึ่งเป็นพารามิเตอร์ประเภทที่ไม่ใช่เทมเพลต (เช่น int
หรือประเภทที่ผู้ใช้กำหนด) จะถูกสร้างขึ้นด้วยไวยากรณ์ T&&
การอ้างอิงค่า R ผูกกับค่า r เท่านั้น
ประเภทการหักด้วยค่า lvalues และ 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)
ดูเพิ่มเติม: std::move
, std::forward
, forwarding references
หรือเป็นที่รู้จัก (อย่างไม่เป็นทางการ) ว่าเป็น ข้อมูลอ้างอิงสากล การอ้างอิงการส่งต่อถูกสร้างขึ้นด้วยไวยากรณ์ T&&
โดยที่ T
คือพารามิเตอร์ประเภทเทมเพลต หรือใช้ auto&&
สิ่งนี้ทำให้สามารถ ส่งต่อได้อย่างสมบูรณ์แบบ : ความสามารถในการส่งผ่านอาร์กิวเมนต์ในขณะที่ยังคงรักษาหมวดหมู่ค่าไว้ (เช่น ค่า l ยังคงเป็นค่า l ค่าชั่วคราวจะถูกส่งต่อเป็นค่า r)
การอ้างอิงการส่งต่ออนุญาตให้การอ้างอิงผูกกับ lvalue หรือ rvalue ขึ้นอยู่กับประเภท การส่งต่อการอ้างอิงเป็นไปตามกฎของ การยุบการอ้างอิง :
T& &
กลายเป็น T&
T& &&
กลายเป็น T&
T&& &
กลายเป็น T&
T&& &&
กลายเป็น T&&
การหักประเภท auto
ด้วยค่า lvalues และ rvalues:
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`
การหักพารามิเตอร์ประเภทเทมเพลตด้วยค่า lvalues และ rvalues:
// 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
ไวยากรณ์ ...
สร้าง แพ็กพารามิเตอร์ หรือขยายหนึ่งแพ็ก ชุดพารามิเตอร์ เทมเพลตคือพารามิเตอร์เทมเพลตที่ยอมรับอาร์กิวเมนต์เทมเพลตตั้งแต่ศูนย์ขึ้นไป (ไม่ใช่ประเภท ประเภท หรือเทมเพลต) เทมเพลตที่มีแพ็กพารามิเตอร์อย่างน้อยหนึ่งชุดเรียกว่า เทมเพลต variadic
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
ประเภทการส่งคืนต่อท้ายในตัวอย่างข้างต้นคือ ประเภทที่ประกาศ (ดูหัวข้อ decltype
) ของนิพจน์ x + y
ตัวอย่างเช่น ถ้า 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 แนะนำประเภทตัวชี้ null ใหม่ที่ออกแบบมาเพื่อแทนที่มาโคร NULL
ของ C nullptr
นั้นเป็นประเภท std::nullptr_t
และสามารถแปลงเป็นประเภทตัวชี้โดยปริยายและไม่เหมือนกับ NULL
ซึ่งไม่สามารถแปลงเป็นประเภทอินทิกรัลได้ยกเว้น bool
void foo ( int );
void foo ( char *);
foo ( NULL ); // error -- ambiguous
foo ( nullptr ); // calls foo(char*)
enums ประเภทที่ปลอดภัยที่แก้ปัญหาที่หลากหลายด้วย enums สไตล์ 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 );
ตอนนี้ตัวสร้างสามารถเรียกตัวสร้างอื่น ๆ ในคลาสเดียวกันโดยใช้รายการ initializer
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 ซึ่งจะมี 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
คือการอ้างอิง lvalue หรือ rvalue
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 อนุญาตให้ใช้ฟังก์ชั่นและแลมบ์ดาเป็นไวยากรณ์ทางเลือกสำหรับการระบุประเภทการส่งคืน
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
(การเคลื่อนไหวการเคลื่อนไหวไม่มีอะไรมากไปกว่าการคัดเลือกนักแสดงอ้างอิง rvalue):
template < typename T>
typename remove_reference<T>::type&& move(T&& arg) {
return static_cast < typename remove_reference<T>::type&&>(arg);
}
การถ่ายโอน std::unique_ptr
s:
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"
Type Traits กำหนดอินเทอร์เฟซที่ใช้เทมเพลตเวลาคอมไพล์เพื่อสืบค้นหรือแก้ไขคุณสมบัติของประเภท
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
Helper เมื่อเทียบกับการใช้ตัวสร้าง ดูส่วนสำหรับ 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`
Tuples เป็นคอลเลกชันขนาดคงที่ของค่าที่แตกต่างกัน เข้าถึงองค์ประกอบของ std::tuple
โดยการแกะกล่องโดยใช้ std::tie
หรือใช้ std::get
// `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"
สร้าง tuple ของการอ้างอิง lvalue มีประโยชน์สำหรับการเปิด std::pair
และ std::tuple
Objects ใช้ 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
s เนื่องจากเหตุผลต่อไปนี้:
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
จากนั้นในตัวชี้ที่ใช้ร่วมกันเราต้องจัดสรรหน่วยความจำสำหรับบล็อกควบคุมภายในตัวชี้ ดูส่วนของ Smart Pointers สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ std::unique_ptr
และ std::shared_ptr
std::ref(val)
ใช้เพื่อสร้างวัตถุประเภท std::reference_wrapper
ที่เก็บข้อมูลอ้างอิงของ Val ใช้ในกรณีที่ผ่านการอ้างอิงตามปกติโดยใช้ &
ไม่รวบรวมหรือ &
ถูกทิ้งเนื่องจากการหักประเภท std::cref
นั้นคล้ายคลึงกัน แต่ Wrapper อ้างอิงที่สร้างขึ้นจะมีการอ้างอิง const ไปยัง Val
// 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 :: เธรด
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
Anthony Calandra
ดู: https://github.com/anthonycalandra/modern-cpp-features/graphs/contributors
เอ็มไอที