C++20 menyertakan fitur bahasa baru berikut:
C++20 menyertakan fitur perpustakaan baru berikut:
C++17 menyertakan fitur bahasa baru berikut:
C++17 menyertakan fitur perpustakaan baru berikut:
C++14 menyertakan fitur bahasa baru berikut:
C++14 menyertakan fitur perpustakaan baru berikut:
C++11 menyertakan fitur bahasa baru berikut:
C++11 menyertakan fitur perpustakaan baru berikut:
Catatan: Meskipun contoh berikut menggambarkan cara menggunakan coroutine pada tingkat dasar, masih banyak lagi yang terjadi saat kode dikompilasi. Contoh-contoh ini tidak dimaksudkan untuk mencakup keseluruhan coroutine C++20. Karena
generator
dan kelastask
belum disediakan oleh perpustakaan standar, saya menggunakan perpustakaan cppcoro untuk mengkompilasi contoh-contoh ini.
Coroutine adalah fungsi khusus yang eksekusinya dapat ditangguhkan dan dilanjutkan. Untuk mendefinisikan coroutine, kata kunci co_return
, co_await
, atau co_yield
harus ada di isi fungsi. Coroutine C++20 tidak dapat ditumpuk; kecuali dioptimalkan oleh kompiler, statusnya dialokasikan di heap.
Contoh coroutine adalah fungsi generator , yang menghasilkan (yaitu menghasilkan) nilai pada setiap pemanggilan:
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;
}
Fungsi generator range
di atas menghasilkan nilai mulai dari start
hingga end
(eksklusif), dengan setiap langkah iterasi menghasilkan nilai saat ini yang disimpan di start
. Generator mempertahankan statusnya di setiap range
pemanggilan (dalam hal ini, pemanggilan adalah untuk setiap iterasi dalam perulangan for). co_yield
mengambil ekspresi tertentu, menghasilkan (yaitu mengembalikan) nilainya, dan menangguhkan coroutine pada saat itu. Setelah melanjutkan, eksekusi dilanjutkan setelah co_yield
.
Contoh lain dari coroutine adalah task , yang merupakan komputasi asinkron yang dijalankan saat tugas ditunggu:
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;
}
Dalam contoh ini, kata kunci co_await
diperkenalkan. Kata kunci ini mengambil ekspresi dan menunda eksekusi jika hal yang Anda tunggu (dalam hal ini, baca atau tulis) belum siap, jika tidak, Anda akan melanjutkan eksekusi. (Perhatikan bahwa di balik terpal, co_yield
menggunakan co_await
.)
Menggunakan tugas untuk mengevaluasi suatu nilai dengan malas:
task< int > calculate_meaning_of_life () {
co_return 42 ;
}
auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
Konsep diberi nama predikat waktu kompilasi yang membatasi tipe. Mereka mengambil bentuk berikut:
template < template-parameter-list >
concept concept-name = constraint-expression;
di mana constraint-expression
dievaluasi menjadi constexpr Boolean. Batasan harus memodelkan persyaratan semantik, seperti apakah suatu tipe berupa numerik atau hashable. Kesalahan kompiler terjadi jika tipe tertentu tidak memenuhi konsep yang diikatnya (yaitu constraint-expression
mengembalikan false
). Karena batasan dievaluasi pada waktu kompilasi, batasan tersebut dapat memberikan pesan kesalahan dan keamanan waktu proses yang lebih bermakna.
// `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>;
Ada berbagai bentuk sintaksis untuk menegakkan konsep:
// 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> () {
// ...
};
Kata kunci requires
digunakan untuk memulai klausa requires
atau ekspresi 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;
}
Perhatikan bahwa daftar parameter dalam ekspresi requires
bersifat opsional. Setiap persyaratan dalam ekspresi requires
adalah salah satu dari berikut ini:
template < typename T>
concept callable = requires (T f) { f (); };
typename
diikuti dengan nama tipe, menegaskan bahwa nama tipe yang diberikan valid. 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
, tentukan batasan tambahan (seperti pada argumen parameter lokal). template < typename T>
concept C = requires(T x) {
requires std::same_as< sizeof (x), size_t >;
};
Lihat juga: perpustakaan konsep.
C++20 memperkenalkan operator pesawat ruang angkasa ( <=>
) sebagai cara baru untuk menulis fungsi perbandingan yang mengurangi boilerplate dan membantu pengembang mendefinisikan semantik perbandingan yang lebih jelas. Mendefinisikan operator perbandingan tiga arah akan menghasilkan fungsi operator perbandingan lainnya secara otomatis (yaitu ==
, !=
, <
, dll.).
Tiga pesanan diperkenalkan:
std::strong_ordering
: Urutan kuat membedakan item yang sama (identik dan dapat dipertukarkan). Memberikan pengurutan less
, greater
, equivalent
, dan equal
. Contoh perbandingan: mencari nilai tertentu dalam daftar, nilai bilangan bulat, string peka huruf besar-kecil.std::weak_ordering
: Urutan yang lemah membedakan item yang setara (tidak identik, tetapi dapat dipertukarkan untuk tujuan perbandingan). Menyediakan pemesanan less
, greater
, dan equivalent
. Contoh perbandingan: string yang tidak peka huruf besar-kecil, pengurutan, membandingkan beberapa tetapi tidak semua anggota kelas yang terlihat.std::partial_ordering
: Pengurutan parsial mengikuti prinsip pengurutan lemah yang sama tetapi mencakup kasus ketika pengurutan tidak memungkinkan. Menyediakan pemesanan less
, greater
, equivalent
, dan unordered
. Contoh perbandingan: nilai floating-point (misalnya NaN
).Operator perbandingan tiga arah default melakukan perbandingan berdasarkan anggota:
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
Anda juga dapat menentukan perbandingan Anda sendiri:
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
Sintaks penginisialisasi yang ditunjuk gaya C. Setiap bidang anggota yang tidak secara eksplisit tercantum dalam daftar penginisialisasi yang ditentukan akan diinisialisasi secara default.
struct A {
int x;
int y;
int z = 123 ;
};
A a {. x = 1 , . z = 2 }; // a.x == 1, a.y == 0, a.z == 2
Gunakan sintaks templat yang familier dalam ekspresi lambda.
auto f = []< typename T>(std::vector<T> v) {
// ...
};
Fitur ini menyederhanakan pola kode umum, membantu menjaga cakupan tetap ketat, dan menawarkan solusi elegan untuk masalah umum seumur hidup.
for ( auto v = std::vector{ 1 , 2 , 3 }; auto & e : v) {
std::cout << e;
}
// prints "123"
Memberikan petunjuk kepada pengoptimal bahwa pernyataan berlabel memiliki kemungkinan besar untuk dieksekusi.
switch (n) {
case 1 :
// ...
break ;
[[likely]] case 2 : // n == 2 is considered to be arbitrarily more
// ... // likely than any other value of n
break ;
}
Jika salah satu atribut yang mungkin/tidak mungkin muncul setelah tanda kurung kanan pada pernyataan if, hal ini menunjukkan bahwa subpernyataan (isi) cabang tersebut kemungkinan/tidak mungkin dieksekusi.
int random = get_random_number_between_x_and_y( 0 , 3 );
if (random > 0 ) [[likely]] {
// body of if statement
// ...
}
Ini juga dapat diterapkan pada subpernyataan (badan) pernyataan iterasi.
while (unlikely_truthy_condition) [[unlikely]] {
// body of while statement
// ...
}
Menangkap this
secara implisit dalam tangkapan lambda menggunakan [=]
sekarang tidak digunakan lagi; lebih suka menangkap secara eksplisit menggunakan [=, this]
atau [=, *this]
.
struct int_value {
int n = 0 ;
auto getter_fn () {
// BAD:
// return [=]() { return n; };
// GOOD:
return [=, * this ]() { return n; };
}
};
Kelas sekarang dapat digunakan dalam parameter templat non-tipe. Objek yang diteruskan sebagai argumen templat memiliki tipe const T
, dengan T
adalah tipe objek, dan memiliki durasi penyimpanan statis.
struct foo {
foo () = default ;
constexpr foo ( int ) {}
};
template <foo f = {}>
auto get_foo () {
return f;
}
get_foo (); // uses implicit constructor
get_foo<foo{ 123 }>();
Fungsi virtual sekarang dapat dibuat constexpr
dan dievaluasi pada waktu kompilasi. fungsi virtual constexpr
dapat menggantikan fungsi virtual non- constexpr
dan sebaliknya.
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
Pilih secara kondisional pada waktu kompilasi apakah konstruktor dibuat eksplisit atau tidak. explicit(true)
sama dengan menentukan 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
Mirip dengan fungsi constexpr
, tetapi fungsi dengan specifier consteval
harus menghasilkan sebuah konstanta. Ini disebut 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
Bawa anggota enum ke dalam ruang lingkup untuk meningkatkan keterbacaan. Sebelum:
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 " ;
}
}
Setelah:
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 " ;
}
}
Tangkap paket parameter berdasarkan nilai:
template < typename ... Args>
auto f (Args&&... args){
// BY VALUE:
return [... args = std::forward<Args>(args)] {
// ...
};
}
Ambil paket parameter dengan referensi:
template < typename ... Args>
auto f (Args&&... args){
// BY REFERENCE:
return [&... args = std::forward<Args>(args)] {
// ...
};
}
Menyediakan tipe standar untuk mewakili string UTF-8.
char8_t utf8_str[] = u8" u0123 " ;
Penentu constinit
mengharuskan suatu variabel harus diinisialisasi pada waktu kompilasi.
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.
Membantu mendukung makro variadik dengan mengevaluasi argumen yang diberikan jika makro variadik tidak kosong.
# define F (...) f( 0 __VA_OPT__ (,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F() // replaced by f(0)
Konsep juga disediakan oleh perpustakaan standar untuk membangun konsep yang lebih rumit. Beberapa diantaranya adalah:
Konsep bahasa inti:
same_as
- menentukan dua tipe yang sama.derived_from
- menentukan bahwa suatu tipe diturunkan dari tipe lain.convertible_to
- menentukan bahwa suatu tipe secara implisit dapat dikonversi ke tipe lain.common_with
- menentukan bahwa dua tipe berbagi tipe yang sama.integral
- menetapkan bahwa suatu tipe adalah tipe integral.default_constructible
- menentukan bahwa suatu objek bertipe dapat dibuat secara default.Konsep perbandingan:
boolean
- menentukan bahwa suatu tipe dapat digunakan dalam konteks Boolean.equality_comparable
- menetapkan bahwa operator==
adalah relasi ekuivalen.Konsep objek:
movable
- menentukan bahwa suatu objek bertipe dapat dipindahkan dan ditukar.copyable
- menentukan bahwa suatu objek bertipe dapat disalin, dipindahkan, dan ditukar.semiregular
- menentukan bahwa suatu objek bertipe dapat disalin, dipindahkan, ditukar, dan dibuat secara default.regular
- menetapkan bahwa suatu tipe adalah regular , yaitu semiregular
dan equality_comparable
.Konsep yang dapat dipanggil:
invocable
- menentukan bahwa tipe callable dapat dipanggil dengan sekumpulan tipe argumen tertentu.predicate
- menentukan bahwa tipe yang dapat dipanggil adalah predikat Boolean.Lihat juga: konsep.
Gabungkan kesederhanaan printf
dengan keamanan tipe iostream
. Menggunakan kurung kurawal sebagai placeholder, dan mendukung pemformatan khusus yang mirip dengan penentu gaya 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);
}
Untuk memformat tipe khusus:
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"
Operasi keluaran buffer untuk aliran keluaran yang dibungkus memastikan sinkronisasi (yaitu tidak ada interleaving keluaran).
std::osyncstream{std::cout} << " The value of x is: " << x << std::endl;
Rentang adalah tampilan (yaitu tidak dimiliki) dari sebuah wadah yang menyediakan akses terbatas ke sekelompok elemen yang berdekatan. Karena tampilan tidak memiliki elemennya sendiri, maka mudah untuk dibuat dan disalin -- cara sederhana untuk memikirkan tampilan adalah bahwa tampilan tersebut menyimpan referensi ke datanya. Berbeda dengan mempertahankan bidang penunjuk/iterator dan panjang, rentang membungkus keduanya dalam satu objek.
Rentang dapat berukuran dinamis atau berukuran tetap (dikenal sebagai batasnya ). Rentang berukuran tetap mendapat manfaat dari pemeriksaan batas.
Span tidak menyebarkan const jadi untuk membuat span read-only gunakan std::span<const T>
.
Contoh: menggunakan rentang berukuran dinamis untuk mencetak bilangan bulat dari berbagai kontainer.
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.
Contoh: rentang berukuran statis akan gagal dikompilasi untuk kontainer yang tidak sesuai dengan rentang rentang tersebut.
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 menyediakan header <bit>
baru yang menyediakan beberapa operasi bit termasuk popcount.
std::popcount ( 0u ); // 0
std::popcount ( 1u ); // 1
std::popcount ( 0b1111'0000u ); // 4
Konstanta matematika termasuk PI, bilangan Euler, dll. ditentukan di header <numbers>
.
std::numbers:: pi ; // 3.14159...
std::numbers::e; // 2.71828...
Fungsi predikat yang benar jika dipanggil dalam konteks waktu kompilasi.
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]`
String (dan tampilan string) sekarang memiliki fungsi anggota starts_with
dan ends_with
untuk memeriksa apakah sebuah string dimulai atau diakhiri dengan string yang diberikan.
std::string str = " foobar " ;
str.starts_with( " foo " ); // true
str.ends_with( " baz " ); // false
Kontainer asosiatif seperti set dan peta memiliki fungsi anggota contains
, yang dapat digunakan sebagai pengganti idiom "temukan dan periksa akhir iterator".
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
Cara yang lebih aman untuk menafsirkan ulang suatu objek dari satu tipe ke tipe lainnya.
float f = 123.0 ;
int i = std::bit_cast< int >(f);
Hitung titik tengah dua bilangan bulat dengan aman (tanpa meluap).
std::midpoint ( 1 , 3 ); // == 2
Mengonversi objek array/"seperti array" tertentu menjadi 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>`
Mengikat N argumen pertama (di mana N adalah jumlah argumen setelah fungsi tertentu ke std::bind_front
) ke fungsi bebas, lambda, atau fungsi anggota tertentu.
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
Menyediakan std::erase
dan/atau std::erase_if
untuk berbagai wadah STL seperti string, daftar, vektor, peta, dll.
Untuk menghapus berdasarkan nilai gunakan std::erase
, atau untuk menentukan predikat kapan harus menghapus elemen gunakan std::erase_if
. Kedua fungsi mengembalikan jumlah elemen yang terhapus.
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}
Fungsi pembantu untuk memberi nama pada hasil perbandingan:
std::is_eq ( 0 <=> 0 ); // == true
std::is_lteq ( 0 <=> 1 ); // == true
std::is_gt ( 0 <=> 1 ); // == false
Lihat juga: perbandingan tiga arah.
Membandingkan dua rentang secara leksikografis menggunakan perbandingan tiga arah dan menghasilkan hasil jenis kategori perbandingan terkuat yang dapat diterapkan.
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
Lihat juga: perbandingan tiga arah, pembantu perbandingan tiga arah.
Pengurangan argumen template otomatis mirip dengan yang dilakukan untuk fungsi, tetapi sekarang termasuk konstruktor kelas.
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>
Mengikuti aturan deduksi auto
, dengan tetap menghormati daftar parameter templat non-tipe dari tipe[*] yang diperbolehkan, argumen templat dapat disimpulkan dari tipe argumennya:
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 >();
* - Misalnya, Anda tidak dapat menggunakan double
sebagai tipe parameter templat, yang juga menjadikan pengurangan ini tidak valid menggunakan auto
.
Ekspresi lipatan melakukan lipatan paket parameter templat melalui operator biner.
(... op e)
atau (e op ...)
, di mana op
adalah operator lipatan dan e
adalah paket parameter yang tidak diperluas, disebut lipatan unary .(e1 op ... op e2)
, dimana op
adalah operator lipatan, disebut lipatan biner . Baik e1
atau e2
merupakan paket parameter yang tidak diperluas, namun tidak keduanya. 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
Perubahan pada deduksi auto
saat digunakan dengan sintaks inisialisasi seragam. Sebelumnya, auto x {3};
menyimpulkan std::initializer_list<int>
, yang sekarang disimpulkan menjadi 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
Lambda waktu kompilasi menggunakan 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
berdasarkan nilai Menangkap this
di lingkungan lambda sebelumnya hanya untuk referensi. Contoh permasalahannya adalah kode asinkron yang menggunakan callback yang mengharuskan objek tersedia, yang mungkin sudah melewati masa pakainya. *this
(C++17) sekarang akan membuat salinan objek saat ini, sementara this
(C++11) terus menangkap dengan referensi.
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
Penentu inline dapat diterapkan pada variabel dan juga fungsi. Variabel yang dideklarasikan inline memiliki semantik yang sama dengan fungsi yang dideklarasikan inline.
// Disassembly example using compiler explorer.
struct S { int x; };
inline S x1 = S{ 321 }; // mov esi, dword ptr [x1]
// x1: .long 321
S x2 = S{ 123 }; // mov eax, dword ptr [.L_ZZ4mainE2x2]
// mov dword ptr [rbp - 8], eax
// .L_ZZ4mainE2x2: .long 123
Ini juga dapat digunakan untuk mendeklarasikan dan mendefinisikan variabel anggota statis, sehingga tidak perlu diinisialisasi dalam file sumber.
struct S {
S () : id{count++} {}
~S () { count--; }
int id;
static inline int count{ 0 }; // declare and initialize count to 0 within the class
};
Menggunakan operator resolusi namespace untuk membuat definisi namespace bersarang.
namespace A {
namespace B {
namespace C {
int i;
}
}
}
Kode di atas dapat ditulis seperti ini:
namespace A ::B::C {
int i;
}
Sebuah proposal untuk mendestrukturisasi inisialisasi, yang memungkinkan penulisan auto [ x, y, z ] = expr;
dimana tipe expr
adalah objek mirip tupel, yang elemennya akan terikat pada variabel x
, y
, dan z
(yang dideklarasikan oleh konstruksi ini). Objek mirip Tuple mencakup std::tuple
, std::pair
, std::array
, dan struktur agregat.
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
}
Versi baru pernyataan if
dan switch
yang menyederhanakan pola kode umum dan membantu pengguna menjaga cakupan tetap ketat.
{
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 ());
}
Tulis kode yang dipakai tergantung pada kondisi waktu kompilasi.
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);
Literal karakter yang dimulai dengan u8
adalah karakter literal bertipe char
. Nilai literal karakter UTF-8 sama dengan nilai titik kode ISO 10646.
char x = u8 ' x ' ;
Enum sekarang dapat diinisialisasi menggunakan sintaks kurung kurawal.
enum byte : unsigned char {};
byte b { 0 }; // OK
byte c {- 1 }; // ERROR
byte d = byte{ 1 }; // OK
byte e = byte{ 256 }; // ERROR
C++17 memperkenalkan tiga atribut baru: [[fallthrough]]
, [[nodiscard]]
dan [[maybe_unused]]
.
[[fallthrough]]
menunjukkan kepada kompiler bahwa kegagalan dalam pernyataan switch adalah perilaku yang diinginkan. Atribut ini hanya dapat digunakan dalam pernyataan switch, dan harus ditempatkan sebelum label case/default berikutnya. switch (n) {
case 1 :
// ...
[[fallthrough]];
case 2 :
// ...
break ;
case 3 :
// ...
[[fallthrough]];
default :
// ...
}
[[nodiscard]]
mengeluarkan peringatan ketika fungsi atau kelas memiliki atribut ini dan nilai kembaliannya dibuang. [[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]]
menunjukkan kepada kompiler bahwa suatu variabel atau parameter mungkin tidak digunakan dan memang dimaksudkan. void my_callback (std::string msg, [[maybe_unused]] bool error) {
// Don't care if `msg` is an error message, just log it.
log (msg);
}
Operator __has_include (operand)
dapat digunakan dalam ekspresi #if
dan #elif
untuk memeriksa apakah header atau file sumber ( operand
) tersedia untuk dimasukkan atau tidak.
Salah satu kasus penggunaannya adalah menggunakan dua perpustakaan yang bekerja dengan cara yang sama, menggunakan perpustakaan cadangan/eksperimental jika perpustakaan pilihan tidak ditemukan di sistem.
# 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
Ini juga dapat digunakan untuk menyertakan header yang ada dengan nama atau lokasi berbeda di berbagai platform, tanpa mengetahui di platform mana program tersebut berjalan, header OpenGL adalah contoh bagus untuk ini yang terletak di direktori OpenGL
di macOS dan GL
di lainnya platform.
# 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
Pengurangan argumen templat kelas (CTAD) memungkinkan kompiler menyimpulkan argumen templat dari argumen konstruktor.
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>*
Untuk tipe yang ditentukan pengguna, panduan deduksi dapat digunakan untuk memandu kompiler cara menyimpulkan argumen templat jika berlaku:
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
Templat kelas std::variant
mewakili union
tipe-aman. Sebuah instance dari std::variant
pada waktu tertentu memiliki nilai dari salah satu tipe alternatifnya (bisa juga tidak bernilai).
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
Templat kelas std::optional
mengelola nilai opsional yang terkandung, yaitu nilai yang mungkin ada atau tidak ada. Kasus penggunaan umum untuk opsional adalah nilai kembalian suatu fungsi yang mungkin gagal.
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 )) {
// ...
}
Kontainer yang aman untuk tipe apa pun untuk nilai tunggal jenis apa pun.
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
Referensi non-pemilik ke sebuah string. Berguna untuk menyediakan abstraksi di atas string (misalnya untuk parsing).
// 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"
Panggil objek Callable
dengan parameter. Contoh objek yang dapat dipanggil adalah std::function
atau lambdas; objek yang dapat dipanggil mirip dengan fungsi biasa.
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
Panggil objek Callable
dengan serangkaian argumen.
auto add = []( int x, int y) {
return x + y;
};
std::apply (add, std::make_tuple( 1 , 2 )); // == 3
Pustaka std::filesystem
yang baru menyediakan cara standar untuk memanipulasi file, direktori, dan jalur dalam sistem file.
Di sini, file besar disalin ke jalur sementara jika ada ruang yang tersedia:
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 " ));
}
}
Tipe std::byte
yang baru menyediakan cara standar untuk merepresentasikan data sebagai byte. Manfaat menggunakan std::byte
atas char
atau unsigned char
adalah bahwa ini bukan tipe karakter, dan juga bukan tipe aritmatika; sedangkan satu-satunya kelebihan operator yang tersedia adalah operasi bitwise.
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
Perhatikan bahwa std::byte
hanyalah sebuah enum, dan inisialisasi enum yang diperkuat menjadi mungkin berkat inisialisasi daftar langsung enum.
Memindahkan node dan menggabungkan container tanpa biaya salinan, pemindahan, atau alokasi/dealokasi heap yang mahal.
Memindahkan elemen dari satu peta ke peta lainnya:
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" } };
Memasukkan seluruh rangkaian:
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 }
Memasukkan elemen yang berumur lebih lama dari wadah:
auto elementFactory () {
std::set<...> s;
s. emplace (...);
return s. extract (s. begin ());
}
s2.insert(elementFactory());
Mengubah kunci elemen peta:
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" } }
Banyak algoritma STL, seperti metode copy
, find
dan sort
, mulai mendukung kebijakan eksekusi paralel : seq
, par
dan par_unseq
yang diterjemahkan menjadi "berurutan", "paralel" dan "paralel tanpa urutan".
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));
Mengambil sampel n elemen dalam urutan tertentu (tanpa pengembalian) dimana setiap elemen mempunyai peluang yang sama untuk dipilih.
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
Jepit nilai tertentu antara batas bawah dan atas.
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
Lipat rentang elemen tertentu. Secara konseptual mirip dengan std::accumulate
, tetapi std::reduce
akan melakukan lipatan secara paralel. Karena pelipatan dilakukan secara paralel, maka jika menentukan operasi biner, maka harus bersifat asosiatif dan komutatif. Operasi biner tertentu juga tidak boleh mengubah elemen apa pun atau membatalkan iterator apa pun dalam rentang tertentu.
Operasi biner default adalah std::plus dengan nilai awal 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
Selain itu, Anda dapat menentukan transformasi untuk reduksi:
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
Dukungan untuk jumlah awalan (pemindaian inklusif dan eksklusif) bersama dengan transformasi.
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
Pembagi persekutuan terbesar (PBT) dan kelipatan persekutuan terkecil (KPK).
const int p = 9 ;
const int q = 3 ;
std::gcd (p, q); // == 3
std::lcm (p, q); // == 9
Fungsi utilitas yang mengembalikan negasi dari hasil fungsi yang diberikan.
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
Ubah integral dan float menjadi string atau sebaliknya. Konversi tidak dapat dibuang, tidak dialokasikan, dan lebih aman dibandingkan dengan yang setara dari pustaka standar C.
Pengguna bertanggung jawab untuk mengalokasikan cukup penyimpanan yang diperlukan untuk std::to_chars
, atau fungsi akan gagal dengan menyetel objek kode kesalahan dalam nilai kembaliannya.
Fungsi-fungsi ini memungkinkan Anda untuk secara opsional meneruskan basis (default ke basis-10) atau penentu format untuk input tipe mengambang.
std::to_chars
mengembalikan pointer char (non-const) yang merupakan salah satu akhir string yang ditulis oleh fungsi di dalam buffer yang diberikan, dan objek kode kesalahan.std::from_chars
mengembalikan pointer const char yang jika berhasil sama dengan pointer akhir yang diteruskan ke fungsi, dan objek kode kesalahan.Kedua objek kode kesalahan yang dikembalikan dari fungsi ini sama dengan objek kode kesalahan yang diinisialisasi secara default jika berhasil.
Ubah angka 123
menjadi 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 */ }
Konversi dari std::string
dengan nilai "123"
menjadi bilangan bulat:
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 */ }
Menyediakan fungsi pembantu abs, round, ceil, dan floor untuk std::chrono::duration
dan 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
Literal biner menyediakan cara mudah untuk merepresentasikan bilangan basis-2. Dimungkinkan untuk memisahkan digit dengan '
.
0b110 // == 6
0b1111'1111 // == 255
C++14 sekarang mengizinkan penentu tipe auto
dalam daftar parameter, mengaktifkan lambda polimorfik.
auto identity = []( auto x) { return x; };
int three = identity( 3 ); // == 3
std::string foo = identity( " foo " ); // == "foo"
Hal ini memungkinkan pembuatan tangkapan lambda yang diinisialisasi dengan ekspresi arbitrer. Nama yang diberikan pada nilai yang diambil tidak perlu dikaitkan dengan variabel apa pun dalam cakupan terlampir dan memperkenalkan nama baru di dalam badan lambda. Ekspresi inisialisasi dievaluasi saat lambda dibuat (bukan saat dipanggil ).
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
Karena sekarang dimungkinkan untuk memindahkan (atau meneruskan ) nilai ke dalam lambda yang sebelumnya hanya dapat ditangkap dengan salinan atau referensi, kini kita dapat menangkap tipe hanya pindahan di lambda berdasarkan nilai. Perhatikan bahwa dalam contoh di bawah ini p
dalam daftar pengambilan task2
di sisi kiri =
adalah variabel baru yang bersifat pribadi untuk badan lambda dan tidak mengacu pada p
asli.
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
Menggunakan tangkapan referensi ini dapat memiliki nama yang berbeda dari variabel yang direferensikan.
auto x = 1 ;
auto f = [&r = x, x = x * 10 ] {
++r;
return r + x;
};
f (); // sets x to 2 and returns 12
Menggunakan tipe pengembalian auto
di C++14, kompiler akan mencoba menyimpulkan tipe tersebut untuk Anda. Dengan lambda, Anda sekarang dapat menyimpulkan tipe pengembaliannya menggunakan auto
, yang memungkinkan pengembalian referensi kesimpulan atau referensi nilai.
// 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`
Penentu tipe decltype(auto)
juga menyimpulkan tipe seperti yang dilakukan auto
. Namun, ini menyimpulkan tipe pengembalian sambil mempertahankan referensi dan kualifikasi cvnya, sedangkan auto
tidak.
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);
Lihat juga: decltype (C++11)
.
Di C++11, isi fungsi constexpr
hanya dapat berisi sekumpulan sintaks yang sangat terbatas, termasuk (namun tidak terbatas pada): typedef
s, using
s, dan satu pernyataan return
. Di C++14, kumpulan sintaksis yang diperbolehkan diperluas hingga mencakup sintaksis yang paling umum seperti pernyataan if
, beberapa return
s, loop, dll.
constexpr int factorial ( int n) {
if (n <= 1 ) {
return 1 ;
} else {
return n * factorial (n - 1 );
}
}
factorial ( 5 ); // == 120
C++14 memungkinkan variabel untuk dijadikan templat:
template < class T >
constexpr T pi = T( 3.1415926535897932385 );
template < class T >
constexpr T e = T( 2.7182818284590452353 );
C++14 memperkenalkan atribut [[deprecated]]
untuk menunjukkan bahwa suatu unit (fungsi, kelas, dll.) tidak disarankan dan kemungkinan besar menghasilkan peringatan kompilasi. Jika alasannya diberikan, maka akan disertakan dalam peringatan.
[[deprecated]]
void old_method ();
[[deprecated( " Use new_method instead " )]]
void legacy_method ();
Literal baru yang ditentukan pengguna untuk tipe pustaka standar, termasuk literal bawaan baru untuk chrono
dan basic_string
. Ini bisa berupa constexpr
artinya dapat digunakan pada waktu kompilasi. Beberapa kegunaan literal ini mencakup penguraian bilangan bulat pada waktu kompilasi, literal biner, dan literal angka imajiner.
using namespace std ::chrono_literals ;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440
Templat kelas std::integer_sequence
mewakili urutan bilangan bulat pada waktu kompilasi. Ada beberapa pembantu yang dibangun di atas:
std::make_integer_sequence<T, N>
- membuat urutan 0, ..., N - 1
dengan tipe T
.std::index_sequence_for<T...>
- mengubah paket parameter templat menjadi urutan bilangan bulat.Ubah array menjadi tuple:
template < typename Array, std:: size_t ... I>
decltype ( auto ) a2t_impl( const Array& a, std::integer_sequence<std:: size_t , I...>) {
return std::make_tuple (a[I]...);
}
template < typename T, std:: size_t N, typename Indices = std::make_index_sequence<N>>
decltype ( auto ) a2t( const std::array<T, N>& a) {
return a2t_impl (a, Indices ());
}
std::make_unique
adalah cara yang disarankan untuk membuat instance std::unique_ptr
s karena alasan berikut:
new
.foo
seperti: foo (std::unique_ptr<T>{ new T{}}, function_that_throws(), std::unique_ptr<T>{ new T{}});
Kompiler bebas memanggil new T{}
, lalu function_that_throws()
, dan seterusnya... Karena kami telah mengalokasikan data di heap pada konstruksi pertama T
, kami telah memperkenalkan kebocoran di sini. Dengan std::make_unique
, kita diberikan keamanan pengecualian:
foo (std::make_unique<T>(), function_that_throws(), std::make_unique<T>());
Lihat bagian tentang smart pointer (C++11) untuk informasi lebih lanjut tentang std::unique_ptr
dan std::shared_ptr
.
Memindahkan suatu objek berarti memindahkan kepemilikan beberapa sumber daya yang dikelolanya ke objek lain.
Manfaat pertama dari semantik perpindahan adalah optimalisasi kinerja. Ketika suatu objek akan mencapai akhir masa pakainya, baik karena bersifat sementara atau karena pemanggilan std::move
secara eksplisit, perpindahan sering kali merupakan cara yang lebih murah untuk mentransfer sumber daya. Misalnya, memindahkan std::vector
hanya menyalin beberapa pointer dan keadaan internal ke vektor baru -- menyalin akan melibatkan keharusan menyalin setiap elemen yang terkandung dalam vektor, yang mahal dan tidak diperlukan jika vektor lama akan segera ada. hancur.
Pemindahan juga memungkinkan tipe yang tidak dapat disalin seperti std::unique_ptr
s (smart pointer) untuk menjamin pada tingkat bahasa bahwa hanya ada satu contoh sumber daya yang dikelola pada satu waktu, sekaligus dapat mentransfer sebuah contoh antar cakupan.
Lihat bagian tentang: referensi nilai, fungsi anggota khusus untuk semantik pemindahan, std::move
, std::forward
, forwarding references
.
C++11 memperkenalkan referensi baru yang disebut rvalue reference . Referensi nilai ke T
, yang merupakan parameter tipe non-templat (seperti int
, atau tipe yang ditentukan pengguna), dibuat dengan sintaksis T&&
. Referensi nilai-nilai hanya mengikat nilai-nilai.
Ketik pengurangan dengan nilai dan nilai:
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)
Lihat juga: std::move
, std::forward
, forwarding references
.
Juga dikenal (secara tidak resmi) sebagai referensi universal . Referensi penerusan dibuat dengan sintaks T&&
di mana T
adalah parameter tipe templat, atau menggunakan auto&&
. Hal ini memungkinkan penerusan sempurna : kemampuan untuk meneruskan argumen sambil mempertahankan kategori nilainya (misalnya nilai tetap sebagai nilai, sementara nilai diteruskan sebagai nilai).
Referensi penerusan memungkinkan referensi untuk mengikat ke nilai atau nilai tergantung pada jenisnya. Penerusan referensi mengikuti aturan runtuhnya referensi :
T& &
menjadi T&
T& &&
menjadi T&
T&& &
menjadi T&
T&& &&
menjadi T&&
pengurangan tipe auto
dengan nilai dan nilai:
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`
Pengurangan parameter tipe template dengan nilai dan nilai:
// 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&&)
Lihat juga: std::move
, std::forward
, rvalue references
.
Sintaks ...
membuat paket parameter atau memperluasnya. Paket parameter templat adalah parameter templat yang menerima nol atau lebih argumen templat (non-tipe, tipe, atau templat). Templat dengan setidaknya satu paket parameter disebut templat variadik .
template < typename ... T>
struct arity {
constexpr static int value = sizeof ...(T);
};
static_assert (arity<>::value == 0 );
static_assert (arity< char , short , int >::value == 3 );
Penggunaan yang menarik untuk ini adalah membuat daftar penginisialisasi dari paket parameter untuk mengulangi argumen fungsi variadik.
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
Wadah elemen ringan seperti array yang dibuat menggunakan sintaksis "daftar kurung". Misalnya, { 1, 2, 3 }
membuat barisan bilangan bulat, yang bertipe std::initializer_list<int>
. Berguna sebagai pengganti meneruskan vektor objek ke suatu fungsi.
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
Pernyataan yang dievaluasi pada waktu kompilasi.
constexpr int x = 0 ;
constexpr int y = 1 ;
static_assert (x == y, " x != y " );
Variabel yang diketik auto
disimpulkan oleh kompiler sesuai dengan jenis penginisialisasinya.
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
Sangat berguna untuk keterbacaan, terutama untuk tipe yang rumit:
std::vector< int > v = ...;
std::vector< int >::const_iterator cit = v.cbegin();
// vs.
auto cit = v.cbegin();
Fungsi juga dapat menyimpulkan tipe pengembalian menggunakan auto
. Di C++11, tipe pengembalian harus ditentukan secara eksplisit, atau menggunakan decltype
seperti:
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
Tipe pengembalian akhir pada contoh di atas adalah tipe yang dideklarasikan (lihat bagian decltype
) dari ekspresi x + y
. Misalnya, jika x
adalah bilangan bulat dan y
adalah bilangan ganda, maka decltype(x + y)
adalah bilangan ganda. Oleh karena itu, fungsi di atas akan menyimpulkan tipenya bergantung pada tipe apa yang dihasilkan oleh ekspresi x + y
. Perhatikan bahwa tipe pengembalian tambahan memiliki akses ke parameternya, dan this
bila diperlukan.
lambda
adalah objek fungsi tanpa nama yang mampu menangkap variabel dalam cakupannya. Ini fitur: daftar pengambilan ; sekumpulan parameter opsional dengan tipe pengembalian tambahan opsional; dan sebuah tubuh. Contoh daftar tangkapan:
[]
- tidak menangkap apa pun.[=]
- menangkap objek lokal (variabel lokal, parameter) dalam cakupan berdasarkan nilai.[&]
- menangkap objek lokal (variabel lokal, parameter) dalam cakupan dengan referensi.[this]
- tangkap this
dengan referensi.[a, &b]
- menangkap objek a
berdasarkan nilai, b
berdasarkan referensi. 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`
Secara default, pengambilan nilai tidak dapat dimodifikasi di dalam lambda karena metode yang dihasilkan kompiler ditandai sebagai const
. Kata kunci mutable
memungkinkan modifikasi variabel yang diambil. Kata kunci ditempatkan setelah daftar parameter (yang harus ada meskipun kosong).
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
adalah operator yang mengembalikan tipe ekspresi yang dideklarasikan yang diteruskan ke sana. kualifikasi dan referensi cv dipertahankan jika merupakan bagian dari ekspresi. Contoh 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`
Lihat juga: decltype(auto) (C++14)
.
Secara semantik mirip dengan penggunaan typedef
, namun alias tipe dengan using
lebih mudah dibaca dan kompatibel dengan templat.
template < typename T>
using Vec = std::vector<T>;
Vec< int > v; // std::vector<int>
using String = std::string;
String s { " foo " };
C++11 memperkenalkan tipe penunjuk null baru yang dirancang untuk menggantikan makro NULL
C. nullptr
sendiri bertipe std::nullptr_t
dan dapat secara implisit diubah menjadi tipe pointer, dan tidak seperti NULL
, tidak dapat diubah menjadi tipe integral kecuali bool
.
void foo ( int );
void foo ( char *);
foo ( NULL ); // error -- ambiguous
foo ( nullptr ); // calls foo(char*)
Enum tipe-aman yang memecahkan berbagai masalah dengan enum gaya C termasuk: konversi implisit, ketidakmampuan untuk menentukan tipe dasar, polusi cakupan.
// 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;
Atribut memberikan sintaks universal lebih dari __attribute__(...)
, __declspec
, dll.
// `noreturn` attribute indicates `f` doesn't return.
[[ noreturn ]] void f () {
throw " error " ;
}
Ekspresi konstan adalah ekspresi yang mungkin dievaluasi oleh kompiler pada waktu kompilasi. Hanya perhitungan non-kompleks yang dapat dilakukan dalam ekspresi konstan (aturan ini semakin rileks dalam versi yang lebih baru). Gunakan Specifier constexpr
untuk menunjukkan variabel, fungsi, dll. Adalah ekspresi konstan.
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
Dalam cuplikan sebelumnya, perhatikan bahwa perhitungan saat memanggil square
dilakukan pada waktu kompilasi, dan kemudian hasilnya tertanam dalam pembuatan kode, sedangkan square2
dipanggil saat run-time.
Nilai constexpr
adalah nilai yang dapat dievaluasi kompiler, tetapi tidak dijamin, pada waktu kompilasi:
const int x = 123 ;
constexpr const int & y = x; // error -- constexpr variable `y` must be initialized by a constant expression
Ekspresi konstan dengan kelas:
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 );
Konstruktor sekarang dapat menghubungi konstruktor lain di kelas yang sama menggunakan daftar inisialisasi.
struct Foo {
int foo;
Foo ( int foo) : foo{foo} {}
Foo () : Foo( 0 ) {}
};
Foo foo;
foo.foo; // == 0
Literal yang ditentukan pengguna memungkinkan Anda untuk memperluas bahasa dan menambahkan sintaks Anda sendiri. Untuk membuat literal, tentukan fungsi T operator "" X(...) { ... }
yang mengembalikan tipe T
, dengan nama X
. Perhatikan bahwa nama fungsi ini mendefinisikan nama literal. Setiap nama literal yang tidak dimulai dengan garis bawah dipesan dan tidak akan dipanggil. Ada aturan tentang parameter apa yang harus diterima oleh fungsi literal yang ditentukan pengguna, sesuai dengan jenis apa yang dipanggil secara literal.
Mengubah Celcius ke Fahrenheit:
// `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
Konversi String ke Integer:
// `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`
Menentukan bahwa fungsi virtual mengesampingkan fungsi virtual lain. Jika fungsi virtual tidak mengganti fungsi virtual orang tua, melempar kesalahan kompiler.
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
};
Menentukan bahwa fungsi virtual tidak dapat diganti dalam kelas yang diturunkan atau bahwa kelas tidak dapat diwarisi.
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
};
Kelas tidak dapat diwarisi dari.
struct A final {};
struct B : A {}; // error -- base 'A' is marked 'final'
Cara yang lebih elegan dan efisien untuk memberikan implementasi fungsional suatu fungsi, seperti konstruktor.
struct A {
A () = default ;
A ( int x) : x{x} {}
int x { 1 };
};
A a; // a.x == 1
A a2 { 123 }; // a.x == 123
Dengan warisan:
struct B {
B () : x{ 1 } {}
int x;
};
struct C : B {
// Calls B::B
C () = default ;
};
C c; // c.x == 1
Cara yang lebih elegan dan efisien untuk memberikan implementasi fungsi yang dihapus. Berguna untuk mencegah salinan pada objek.
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
Gula sintaksis untuk iterasi di atas elemen wadah.
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int & x : a) x *= 2 ;
// a == { 2, 4, 6, 8, 10 }
Perhatikan perbedaan saat menggunakan int
sebagai lawan int&
:
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int x : a) x *= 2 ;
// a == { 1, 2, 3, 4, 5 }
Konstruktor copy dan operator penugasan salinan dipanggil ketika salinan dibuat, dan dengan pengenalan semantik MOVE C ++ 11, sekarang ada konstruktor pindah dan operator penugasan untuk pindah.
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
Konversi konstruktor akan mengonversi nilai sintaksis daftar buatan menjadi argumen konstruktor.
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)
Perhatikan bahwa sintaksis daftar yang diikat tidak memungkinkan penyempitan:
struct A {
A ( int ) {}
};
A a ( 1.1 ); // OK
A b { 1.1 }; // Error narrowing conversion from double to int
Perhatikan bahwa jika seorang konstruktor menerima std::initializer_list
, itu akan dipanggil sebagai gantinya:
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>)
Fungsi konversi sekarang dapat dibuat eksplisit menggunakan spesifikasi 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()
Semua anggota namespace inline diperlakukan seolah -olah mereka adalah bagian dari namespace induknya, yang memungkinkan spesialisasi fungsi dan mengurangi proses versi. Ini adalah properti transitif, jika A berisi B, yang pada gilirannya berisi C dan B dan C adalah inline namespaces, anggota C dapat digunakan seolah -olah mereka berada di 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
Memungkinkan anggota data non-statis untuk diinisialisasi di mana mereka dinyatakan, berpotensi membersihkan konstruktor inisialisasi default.
// 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 sekarang dapat menyimpulkan ketika serangkaian tanda kurung sudut kanan digunakan sebagai operator atau sebagai pernyataan penutupan typedef, tanpa harus menambahkan spasi putih.
typedef std::map< int , std::map < int , std::map < int , int > > > cpp98LongTypedef;
typedef std::map< int , std::map < int , std::map < int , int >>> cpp11LongTypedef;
Fungsi anggota sekarang dapat memenuhi syarat tergantung pada apakah *this
adalah referensi LValue atau 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 memungkinkan fungsi dan lambdas sintaks alternatif untuk menentukan jenis pengembaliannya.
int f () {
return 123 ;
}
// vs.
auto f () -> int {
return 123 ;
}
auto g = []() -> int {
return 123 ;
};
Fitur ini sangat berguna ketika tipe pengembalian tertentu tidak dapat diselesaikan:
// 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;
}
Dalam C ++ 14, decltype(auto) (C++14)
dapat digunakan sebagai gantinya.
noexcept
Specifier menentukan apakah suatu fungsi dapat melempar pengecualian. Ini adalah versi yang lebih baik dari 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
Fungsi non-melempar diizinkan untuk memanggil fungsi yang berpotensi melempar. Setiap kali pengecualian dilemparkan dan pencarian penangan menemukan blok terluar dari fungsi non-pelemparan, fungsi std :: diakhiri dipanggil.
extern void f (); // potentially-throwing
void g () noexcept {
f (); // valid, even if f throws
throw 42 ; // valid, effectively a call to std::terminate
}
Memberikan tipe standar untuk mewakili string UTF-8.
char32_t utf8_str[] = U" u0123 " ;
char16_t utf8_str[] = u" u0123 " ;
C ++ 11 memperkenalkan cara baru untuk mendeklarasikan literal string sebagai "literal string mentah". Karakter yang dikeluarkan dari urutan melarikan diri (tab, umpan garis, latar belakang tunggal, dll.) Dapat dimasukkan mentah saat melestarikan pemformatan. Ini berguna, misalnya, untuk menulis teks sastra, yang mungkin berisi banyak kutipan atau pemformatan khusus. Ini dapat membuat literal string Anda lebih mudah dibaca dan dipelihara.
Literal string mentah dinyatakan menggunakan sintaks berikut:
R"delimiter(raw_characters)delimiter"
Di mana:
delimiter
adalah urutan karakter opsional yang terbuat dari karakter sumber apa pun kecuali tanda kurung, backslash dan spasi.raw_characters
adalah urutan karakter mentah apa pun; tidak boleh berisi urutan penutupan ")delimiter"
.Contoh:
// msg1 and msg2 are equivalent.
const char * msg1 = " n Hello, nt world! n " ;
const char * msg2 = R"(
Hello,
world!
)" ;
std::move
menunjukkan bahwa objek yang diteruskan ke sana dapat ditransfer sumber daya. Menggunakan benda-benda yang telah dipindahkan harus digunakan dengan hati-hati, karena mereka dapat dibiarkan dalam keadaan yang tidak ditentukan (lihat: apa yang dapat saya lakukan dengan objek yang dipindahkan-dari?).
Definisi std::move
(melakukan gerakan tidak lebih dari casting ke referensi rvalue):
template < typename T>
typename remove_reference<T>::type&& move(T&& arg) {
return static_cast < typename remove_reference<T>::type&&>(arg);
}
Mentransfer 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`
Mengembalikan argumen yang diteruskan ke sana sambil mempertahankan kategori nilai dan kualifikasi CV. Berguna untuk kode dan pabrik generik. Digunakan bersama dengan forwarding references
.
Definisi std::forward
:
template < typename T>
T&& forward( typename remove_reference<T>::type& arg) {
return static_cast <T&&>(arg);
}
Contoh wrapper
fungsi yang hanya meneruskan A
lain ke salinan atau pindahkan A
baru: Konstruktor:
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
Lihat juga: forwarding references
, rvalue references
.
Perpustakaan std::thread
menyediakan cara standar untuk mengontrol utas, seperti memalukan dan membunuhnya. Dalam contoh di bawah ini, beberapa utas diteluskan untuk melakukan perhitungan yang berbeda dan kemudian program menunggu semuanya selesai.
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
}
Mengubah argumen numerik ke std::string
std::to_string ( 1.2 ); // == "1.2"
std::to_string ( 123 ); // == "123"
Ketik sifat menentukan antarmuka berbasis template compile-time untuk meminta atau memodifikasi sifat-sifat jenis.
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 memperkenalkan pointer pintar baru: std::unique_ptr
, std::shared_ptr
, std::weak_ptr
. std::auto_ptr
sekarang menjadi sudah usang dan akhirnya dihapus dalam C ++ 17.
std::unique_ptr
adalah penunjuk yang tidak dapat digerakkan dan bergerak yang mengelola memori yang dialokasikan oleh heap sendiri. Catatan: Lebih suka menggunakan fungsi helper std::make_X
daripada menggunakan konstruktor. Lihat bagian untuk std :: make_unique dan 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
adalah penunjuk pintar yang mengelola sumber daya yang dibagikan di beberapa pemilik. Pointer bersama memegang blok kontrol yang memiliki beberapa komponen seperti objek yang dikelola dan penghitung referensi. Semua akses blok kontrol-aman, namun, memanipulasi objek yang dikelola itu sendiri tidak aman.
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);
Perpustakaan Chrono berisi serangkaian fungsi dan jenis utilitas yang berhubungan dengan durasi , jam , dan titik waktu . Salah satu kasus penggunaan Perpustakaan ini adalah Kode Benchmarking:
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`
Tupel adalah kumpulan nilai heterogen ukuran tetap. Akses elemen std::tuple
dengan membongkar menggunakan std::tie
, atau menggunakan 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"
Membuat tuple referensi lvalue. Berguna untuk membongkar std::pair
dan std::tuple
objek. Gunakan std::ignore
sebagai placeholder untuk nilai -nilai yang diabaikan. Dalam C ++ 17, binding terstruktur harus digunakan sebagai gantinya.
// 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
adalah wadah yang dibangun di atas array gaya-C. Mendukung operasi kontainer umum seperti penyortiran.
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 }
Wadah ini mempertahankan kompleksitas rata-rata waktu konstan untuk mencari, memasukkan, dan menghapus operasi. Untuk mencapai kompleksitas waktu konstan, pengorbanan pesanan untuk kecepatan dengan hashing elemen ke dalam ember. Ada empat wadah yang tidak tertib:
unordered_set
unordered_multiset
unordered_map
unordered_multimap
std::make_shared
adalah cara yang disarankan untuk membuat contoh std::shared_ptr
S karena alasan berikut:
new
.foo
itu: foo (std::shared_ptr<T>{ new T{}}, function_that_throws(), std::shared_ptr<T>{ new T{}});
Kompiler bebas untuk memanggil new T{}
, lalu function_that_throws()
, dan seterusnya ... karena kami telah mengalokasikan data pada tumpukan dalam konstruksi pertama T
, kami telah memperkenalkan kebocoran di sini. Dengan std::make_shared
, kami diberi keselamatan pengecualian:
foo (std::make_shared<T>(), function_that_throws(), std::make_shared<T>());
std::shared_ptr{ new T{} }
, kita harus mengalokasikan memori untuk T
, lalu di pointer bersama kita harus mengalokasikan memori untuk blok kontrol di dalam pointer. Lihat bagian Pointer Cerdas untuk informasi lebih lanjut tentang std::unique_ptr
dan std::shared_ptr
.
std::ref(val)
digunakan untuk membuat objek tipe std::reference_wrapper
yang memiliki referensi val. Digunakan dalam kasus ketika referensi biasa lewat menggunakan &
tidak menyusun atau &
dijatuhkan karena pengurangan jenis. std::cref
serupa tetapi dibuat pembungkus referensi memegang referensi const ke 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 memperkenalkan model memori untuk C ++, yang berarti dukungan perpustakaan untuk operasi threading dan atom. Beberapa operasi ini termasuk (tetapi tidak terbatas pada) beban/toko atom, perbandingan-dan-swap, bendera atom, janji, berjangka, kunci, dan variabel kondisi.
Lihat bagian di: std :: thread
std::async
menjalankan fungsi yang diberikan baik secara tidak sinkron atau malas dievaluasi, lalu mengembalikan std::future
yang memiliki hasil dari panggilan fungsi itu.
Parameter pertama adalah kebijakan yang bisa:
std::launch::async | std::launch::deferred
tergantung pada implementasi apakah akan melakukan eksekusi asinkron atau evaluasi malas.std::launch::async
Jalankan objek yang dapat dipanggil pada utas baru.std::launch::deferred
melakukan evaluasi malas di utas saat ini. 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
and std::end
free functions ditambahkan untuk mengembalikan dan mengakhiri iterator wadah secara umum. Fungsi -fungsi ini juga bekerja dengan array mentah yang tidak memiliki fungsi anggota begin
dan 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
Lihat: https://github.com/anthonycalandra/modern-cpp-features/graphs/contributors
MIT