يتضمن 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
موجودة في نص الوظيفة. كوروتينات C++ 20 غير مكدسة؛ ما لم يتم تحسينها بواسطة المترجم، يتم تخصيص حالتها على الكومة.
مثال على coroutine هو دالة المولد ، والتي تنتج (أي تولد) قيمة عند كل استدعاء:
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
(في هذه الحالة، يكون الاستدعاء لكل تكرار في الحلقة). يأخذ co_yield
التعبير المحدد، وينتج (على سبيل المثال) قيمته، ويعلق coroutine عند تلك النقطة. عند الاستئناف، يستمر التنفيذ بعد co_yield
.
مثال آخر على الكوروتين هو المهمة ، وهي عملية حسابية غير متزامنة يتم تنفيذها عند انتظار المهمة:
task< void > echo (socket s) {
for (;;) {
auto data = co_await s. async_read ();
co_await async_write (s, data);
}
// Implicit co_return at the end of this function:
// co_return;
}
في هذا المثال، تم تقديم الكلمة الأساسية co_await
. تأخذ هذه الكلمة الأساسية تعبيرًا وتوقف التنفيذ إذا كان الشيء الذي تنتظره (في هذه الحالة، القراءة أو الكتابة) غير جاهز، وإلا فستستمر في التنفيذ. (لاحظ أنه تحت الغطاء، يستخدم co_yield
co_await
.)
استخدام مهمة لتقييم القيمة بتكاسل:
task< int > calculate_meaning_of_life () {
co_return 42 ;
}
auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
تتم تسمية المفاهيم بمسندات وقت الترجمة التي تقيد الأنواع. يأخذون الشكل التالي:
template < template-parameter-list >
concept concept-name = constraint-expression;
حيث يتم تقييم constraint-expression
إلى constexpr 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 class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string (rgba_color_channel channel) {
switch (channel) {
case rgba_color_channel::red: return " red " ;
case rgba_color_channel::green: return " green " ;
case rgba_color_channel::blue: return " blue " ;
case rgba_color_channel::alpha: return " alpha " ;
}
}
بعد:
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string (rgba_color_channel my_channel) {
switch (my_channel) {
using enum rgba_color_channel;
case red: return " red " ;
case green: return " green " ;
case blue: return " blue " ;
case alpha: return " alpha " ;
}
}
التقاط حزم المعلمات حسب القيمة:
template < typename ... Args>
auto f (Args&&... args){
// BY VALUE:
return [... args = std::forward<Args>(args)] {
// ...
};
}
التقاط حزم المعلمات حسب المرجع:
template < typename ... Args>
auto f (Args&&... args){
// BY REFERENCE:
return [&... args = std::forward<Args>(args)] {
// ...
};
}
يوفر نوعًا قياسيًا لتمثيل سلاسل UTF-8.
char8_t utf8_str[] = u8" u0123 " ;
يتطلب محدد constinit
وجوب تهيئة المتغير في وقت الترجمة.
const char * g () { return " dynamic initialization " ; }
constexpr const char * f ( bool p) { return p ? " constant initializer " : g (); }
constinit const char * c = f( true ); // OK
constinit const char * d = g( false ); // ERROR: `g` is not constexpr, so `d` cannot be evaluated at compile-time.
يساعد في دعم وحدات الماكرو المتغيرة من خلال التقييم للوسيطة المحددة إذا كان الماكرو المتغير غير فارغ.
# define F (...) f( 0 __VA_OPT__ (,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F() // replaced by f(0)
يتم توفير المفاهيم أيضًا من خلال المكتبة القياسية لبناء مفاهيم أكثر تعقيدًا. بعض هذه تشمل:
مفاهيم اللغة الأساسية:
same_as
- يحدد نوعين متماثلين.derived_from
- يحدد أن النوع مشتق من نوع آخر.convertible_to
- يحدد أن النوع قابل للتحويل ضمنيًا إلى نوع آخر.common_with
- يحدد أن النوعين يشتركان في نوع مشترك.integral
- يحدد أن النوع هو نوع متكامل.default_constructible
- يحدد أنه يمكن إنشاء كائن من النوع بشكل افتراضي.مفاهيم المقارنة:
boolean
- يحدد أنه يمكن استخدام النوع في السياقات المنطقية.equality_comparable
- تحدد أن operator==
هو علاقة تكافؤ.مفاهيم الكائن:
movable
- يحدد أنه يمكن نقل كائن من النوع وتبديله.copyable
- يحدد أنه يمكن نسخ كائن من النوع ونقله وتبديله.semiregular
- يحدد أنه يمكن نسخ كائن من النوع ونقله وتبديله وإنشاءه بشكل افتراضي.regular
- يحدد أن النوع عادي ، أي أنه semiregular
equality_comparable
.المفاهيم القابلة للاستدعاء:
invocable
- يحدد أنه يمكن استدعاء النوع القابل للاستدعاء مع مجموعة معينة من أنواع الوسائط.predicate
- يحدد أن النوع القابل للاستدعاء هو مسند منطقي.أنظر أيضا: المفاهيم.
اجمع بين بساطة printf
وأمان النوع الذي يوفره iostream
. يستخدم الأقواس كعناصر نائبة، ويدعم التنسيق المخصص المشابه لمحددات نمط printf.
std::format ( " {1} {0} " , " world " , " hello " ); // == "hello world"
int x = 123 ;
std::string str = std::format( " x: {} " , x); // str == "x: 123"
// Format to an output iterator:
for ( auto x : { 1 , 2 , 3 }) {
std::format_to (std::ostream_iterator< char >{std::cout, " n " }, " {} " , x);
}
لتنسيق الأنواع المخصصة:
struct fraction {
int numerator;
int denominator;
};
template <>
struct std ::formatter<fraction>
{
constexpr auto parse (std::format_parse_context& ctx) {
return ctx. begin ();
}
auto format ( const fraction& f, std::format_context& ctx) const {
return std::format_to (ctx. out (), " {0:d}/{1:d} " , f. numerator , f. denominator );
}
};
fraction f{ 1 , 2 };
std::format ( " {} " , f); // == "1/2"
مخازن مؤقتة لعمليات الإخراج لتيار الإخراج المغلف لضمان التزامن (أي عدم تشذير الإخراج).
std::osyncstream{std::cout} << " The value of x is: " << x << std::endl;
الامتداد عبارة عن عرض (أي غير مملوك) لحاوية توفر وصولاً محدد الحدود إلى مجموعة متجاورة من العناصر. نظرًا لأن طرق العرض لا تمتلك عناصرها، فهي رخيصة البناء والنسخ - والطريقة المبسطة للتفكير في طرق العرض هي أنها تحتوي على مراجع لبياناتها. بدلاً من الحفاظ على المؤشر/المكرر وحقل الطول، فإن الامتداد يلتف كلاهما في كائن واحد.
يمكن أن تكون الامتدادات ذات حجم ديناميكي أو ذات حجم ثابت (يُعرف باسم مداها ). تستفيد الامتدادات ذات الحجم الثابت من فحص الحدود.
لا يقوم Span بنشر const، لذا لإنشاء نطاق للقراءة فقط، استخدم 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>
جديد يوفر بعض عمليات البت بما في ذلك عدد النوافذ.
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
يحول كائن الصفيف/"المشابه للصفيف" المحدد إلى std::array
.
std::to_array ( " foo " ); // returns `std::array<char, 4>`
std::to_array< int >({ 1 , 2 , 3 }); // returns `std::array<int, 3>`
int a[] = { 1 , 2 , 3 };
std::to_array (a); // returns `std::array<int, 3>`
يربط وسيطات N الأولى (حيث N هو عدد الوسائط بعد الوظيفة المحددة لـ std::bind_front
) إلى وظيفة مجانية معينة، أو lambda، أو وظيفة عضو.
const auto f = []( int a, int b, int c) { return a + b + c; };
const auto g = std::bind_front(f, 1 , 1 );
g ( 1 ); // == 3
يوفر 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
عبارة عن حزمة معلمات غير موسعة، اسم الطيات الأحادية .(e1 op ... op e2)
، حيث يكون op
عبارة عن عوامل طي، اسم الطية الثنائية . إما e1
أو e2
عبارة عن حزمة معلمات غير موسعة، ولكن ليس كليهما. template < typename ... Args>
bool logicalAnd (Args... args) {
// Binary folding.
return ( true && ... && args);
}
bool b = true ;
bool & b2 = b;
logicalAnd (b, b2, true ); // == true
template < typename ... Args>
auto sum (Args... args) {
// Unary folding.
return (... + args);
}
sum ( 1.0 , 2 . 0f , 3 ); // == 6.0
تغييرات على الخصم auto
عند استخدامه مع صيغة التهيئة الموحدة. سابقًا، auto x {3};
يستنتج std::initializer_list<int>
، والذي يستنتج الآن إلى int
.
auto x1 { 1 , 2 , 3 }; // error: not a single element
auto x2 = { 1 , 2 , 3 }; // x2 is std::initializer_list<int>
auto x3 { 3 }; // x3 is int
auto x4 { 3.0 }; // x4 is double
ترجمة وقت lambdas باستخدام 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 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 (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
over 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
هو مجرد تعداد، وتصبح التهيئة المعززة للتعدادات ممكنة بفضل التهيئة المباشرة للقائمة للتعدادات.
نقل العقد ودمج الحاويات دون تحمل تكاليف النسخ أو النقل أو تخصيص الكومة/إلغاء التخصيص الباهظة الثمن.
نقل العناصر من خريطة إلى أخرى:
std::map< int , string> src {{ 1 , " one " }, { 2 , " two " }, { 3 , " buckle my shoe " }};
std::map< int , string> dst {{ 3 , " three " }};
dst.insert(src.extract(src.find( 1 ))); // Cheap remove and insert of { 1, "one" } from `src` to `dst`.
dst.insert(src.extract( 2 )); // Cheap remove and insert of { 2, "two" } from `src` to `dst`.
// dst == { { 1, "one" }, { 2, "two" }, { 3, "three" } };
إدراج مجموعة كاملة:
std::set< int > src { 1 , 3 , 5 };
std::set< int > dst { 2 , 4 , 5 };
dst.merge(src);
// src == { 5 }
// dst == { 1, 2, 3, 4, 5 }
إدراج العناصر التي تدوم أطول من الحاوية:
auto elementFactory () {
std::set<...> s;
s. emplace (...);
return s. extract (s. begin ());
}
s2.insert(elementFactory());
تغيير مفتاح عنصر الخريطة:
std::map< int , string> m {{ 1 , " one " }, { 2 , " two " }, { 3 , " three " }};
auto e = m.extract( 2 );
e.key() = 4 ;
m.insert(std::move(e));
// m == { { 1, "one" }, { 3, "three" }, { 4, "two" } }
بدأت العديد من خوارزميات STL، مثل طرق copy
find
sort
، في دعم سياسات التنفيذ المتوازي : seq
و par
و par_unseq
والتي تُترجم إلى "بالتسلسل" و"بالتوازي" و"بالتوازي غير المتسلسل".
std::vector< int > longVector;
// Find element using parallel execution policy
auto result1 = std::find(std::execution::par, std::begin(longVector), std::end(longVector), 2 );
// Sort elements using sequential execution policy
auto result2 = std::sort(std::execution::seq, std::begin(longVector), std::end(longVector));
عينات n من العناصر في التسلسل المحدد (بدون استبدال) حيث يكون لكل عنصر فرصة متساوية في الاختيار.
const std::string ALLOWED_CHARS = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 " ;
std::string guid;
// Sample 5 characters from ALLOWED_CHARS.
std::sample (ALLOWED_CHARS.begin(), ALLOWED_CHARS.end(), std::back_inserter(guid),
5, std::mt19937{ std::random_device{}() });
std::cout << guid; // e.g. G1fW2
المشبك قيمة معينة بين الحد الأدنى والأعلى.
std::clamp ( 42 , - 1 , 1 ); // == 1
std::clamp (- 42 , - 1 , 1 ); // == -1
std::clamp ( 0 , - 1 , 1 ); // == 0
// `std::clamp` also accepts a custom comparator:
std::clamp ( 0 , - 1 , 1 , std::less<>{}); // == 0
أضعاف على نطاق معين من العناصر. يشبه من الناحية النظرية std::accumulate
، لكن std::reduce
سيؤدي إلى إجراء الطية بالتوازي. نظرًا لأن الطية تتم بالتوازي، إذا قمت بتحديد عملية ثنائية، فيجب أن تكون ترابطية وإبدالية. يجب أيضًا ألا تؤدي العملية الثنائية المحددة إلى تغيير أي عنصر أو إبطال أي مكررات ضمن النطاق المحدد.
العملية الثنائية الافتراضية هي std::plus بقيمة أولية 0.
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::reduce (std::cbegin(a), std::cend(a)); // == 6
// Using a custom binary op:
std::reduce (std::cbegin(a), std::cend(a), 1, std::multiplies<>{}); // == 6
بالإضافة إلى ذلك، يمكنك تحديد التحويلات للمخفضات:
std::transform_reduce (std::cbegin(a), std::cend(a), 0, std::plus<>{}, times_ten); // == 60
const std::array< int , 3 > b{ 1 , 2 , 3 };
const auto product_times_ten = []( const auto a, const auto b) { return a * b * 10 ; };
std::transform_reduce (std::cbegin(a), std::cend(a), std::cbegin(b), 0, std::plus<>{}, product_times_ten); // == 140
دعم مجاميع البادئات (عمليات المسح الشاملة والحصرية) بالإضافة إلى التحويلات.
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}); // 1 3 6
std::exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}); // 0 1 3
const auto times_ten = []( const auto n) { return n * 10 ; };
std::transform_inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}, times_ten); // 10 30 60
std::transform_exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}, times_ten); // 0 10 30
القاسم المشترك الأكبر (GCD) والمضاعف المشترك الأصغر (LCM).
const int p = 9 ;
const int q = 3 ;
std::gcd (p, q); // == 3
std::lcm (p, q); // == 9
دالة مساعدة تقوم بإرجاع نفي نتيجة الدالة المحددة.
const std::ostream_iterator< int > ostream_it{ std::cout, " " };
const auto is_even = []( const auto n) { return n % 2 == 0 ; };
std::vector< int > v{ 0 , 1 , 2 , 3 , 4 };
// Print all even numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, is_even); // 0 2 4
// Print all odd (not even) numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, std::not_fn(is_even)); // 1 3
تحويل التكاملات والعوامات إلى سلسلة أو العكس. التحويلات غير قابلة للطرح، ولا يتم تخصيصها، وهي أكثر أمانًا من نظيراتها من مكتبة C القياسية.
يتحمل المستخدمون مسؤولية تخصيص مساحة تخزين كافية مطلوبة لـ std::to_chars
، وإلا ستفشل الوظيفة عن طريق تعيين كائن رمز الخطأ في قيمة الإرجاع الخاصة به.
تسمح لك هذه الوظائف بتمرير قاعدة اختيارية (افتراضية إلى قاعدة 10) أو محدد تنسيق لإدخال النوع العائم.
std::to_chars
بإرجاع مؤشر char (غير ثابت) وهو ما يمثل نهاية السلسلة التي كتبتها الدالة داخل المخزن المؤقت المحدد، وكائن رمز الخطأ.std::from_chars
مؤشر const char والذي يساوي عند النجاح مؤشر النهاية الذي تم تمريره إلى الوظيفة وكائن رمز الخطأ.كلا كائني رمز الخطأ الذي تم إرجاعه من هذه الوظائف يساوي كائن رمز الخطأ الذي تمت تهيئته افتراضيًا عند النجاح.
تحويل الرقم 123
إلى std::string
:
const int n = 123 ;
// Can use any container, string, array, etc.
std::string str;
str.resize( 3 ); // hold enough storage for each digit of `n`
const auto [ ptr, ec ] = std::to_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << str << std::endl; } // 123
else { /* handle failure */ }
التحويل من 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
في قائمة المعلمات، مما يتيح لامدا متعددة الأشكال.
auto identity = []( auto x) { return x; };
int three = identity( 3 ); // == 3
std::string foo = identity( " foo " ); // == "foo"
يسمح هذا بإنشاء لقطات لامدا تمت تهيئتها بتعبيرات عشوائية. لا يلزم أن يكون الاسم المعطى للقيمة الملتقطة مرتبطًا بأي متغيرات في النطاقات المتضمنة ويقدم اسمًا جديدًا داخل نص لامدا. يتم تقييم تعبير التهيئة عند إنشاء lambda (وليس عند استدعائه ).
int factory ( int i) { return i * 10 ; }
auto f = [x = factory( 2 )] { return x; }; // returns 20
auto generator = [x = 0 ] () mutable {
// this would not compile without 'mutable' as we are modifying x on each call
return x++;
};
auto a = generator(); // == 0
auto b = generator(); // == 1
auto c = generator(); // == 2
نظرًا لأنه أصبح من الممكن الآن نقل (أو إعادة توجيه ) القيم إلى لامدا التي كان من الممكن التقاطها سابقًا فقط عن طريق النسخ أو المرجع، يمكننا الآن التقاط أنواع النقل فقط في لامدا حسب القيمة. لاحظ أنه في المثال أدناه p
في قائمة الالتقاط task2
على الجانب الأيسر من =
هو متغير جديد خاص بنص lambda ولا يشير إلى p
الأصلي.
auto p = std::make_unique< int >( 1 );
auto task1 = [=] { *p = 5 ; }; // ERROR: std::unique_ptr cannot be copied
// vs.
auto task2 = [p = std::move(p)] { *p = 5 ; }; // OK: p is move-constructed into the closure object
// the original p is empty after task2 is created
يمكن أن يكون لاستخدام هذه اللقطات المرجعية أسماء مختلفة عن المتغير المشار إليه.
auto x = 1 ;
auto f = [&r = x, x = x * 10 ] {
++r;
return r + x;
};
f (); // sets x to 2 and returns 12
باستخدام نوع الإرجاع auto
في C++ 14، سيحاول المترجم استنتاج النوع لك. باستخدام lambdas، يمكنك الآن استنتاج نوع الإرجاع الخاص به باستخدام auto
، مما يجعل إرجاع مرجع مستنتج أو مرجع rvalue ممكنًا.
// 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
. ومع ذلك، فإنه يستنتج أنواع الإرجاع مع الاحتفاظ بمراجعها ومؤهلات السيرة الذاتية، بينما لا يفعل ذلك 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
، return
متعددة، والحلقات، وما إلى ذلك.
constexpr int factorial ( int n) {
if (n <= 1 ) {
return 1 ;
} else {
return n * factorial (n - 1 );
}
}
factorial ( 5 ); // == 120
يسمح C++ 14 بتشكيل المتغيرات:
template < class T >
constexpr T pi = T( 3.1415926535897932385 );
template < class T >
constexpr T e = T( 2.7182818284590452353 );
يقدم C++ 14 السمة [[deprecated]]
للإشارة إلى عدم تشجيع الوحدة (الوظيفة، الفئة، وما إلى ذلك) ومن المحتمل أن تؤدي إلى تحذيرات التجميع. إذا تم تقديم السبب، فسيتم تضمينه في التحذيرات.
[[deprecated]]
void old_method ();
[[deprecated( " Use new_method instead " )]]
void legacy_method ();
قيم حرفية جديدة محددة من قبل المستخدم لأنواع المكتبات القياسية، بما في ذلك القيم الحرفية الجديدة المضمنة لـ chrono
و basic_string
. يمكن أن تكون هذه constexpr
مما يعني أنه يمكن استخدامها في وقت الترجمة. تتضمن بعض الاستخدامات لهذه القيم الحرفية تحليل الأعداد الصحيحة في وقت الترجمة، والقيم الحرفية الثنائية، والقيم الحرفية للأرقام التخيلية.
using namespace std ::chrono_literals ;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440
يمثل قالب الفئة std::integer_sequence
تسلسل وقت الترجمة للأعداد الصحيحة. هناك عدد قليل من المساعدين المبنيين على القمة:
std::make_integer_sequence<T, N>
- ينشئ تسلسلًا من 0, ..., N - 1
بالنوع T
.std::index_sequence_for<T...>
- يحول حزمة معلمات القالب إلى تسلسل صحيح.تحويل مصفوفة إلى Tuple:
template < typename Array, std:: size_t ... I>
decltype ( auto ) a2t_impl( const Array& a, std::integer_sequence<std:: size_t , I...>) {
return std::make_tuple (a[I]...);
}
template < typename T, std:: size_t N, typename Indices = std::make_index_sequence<N>>
decltype ( auto ) a2t( const std::array<T, N>& a) {
return a2t_impl (a, Indices ());
}
std::make_unique
هي الطريقة الموصى بها لإنشاء مثيلات std::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 (المؤشرات الذكية) ضمان وجود مثيل واحد فقط للمورد الذي تتم إدارته في المرة الواحدة على مستوى اللغة، مع القدرة على نقل مثيل بين النطاقات.
راجع الأقسام المتعلقة بمراجع rvalue، ووظائف الأعضاء الخاصة لدلالات النقل، std::move
، std::forward
، forwarding references
.
يقدم C++11 مرجعًا جديدًا يسمى مرجع rvalue . يتم إنشاء مرجع rvalue إلى T
، وهو معلمة نوع غير قالب (مثل int
أو نوع محدد من قبل المستخدم)، باستخدام بناء الجملة T&&
. ترتبط مراجع Rvalue بقيم r فقط.
اكتب الخصم باستخدام القيم l وقيم r:
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&&
. وهذا يتيح إعادة توجيه مثالية : القدرة على تمرير الوسائط مع الحفاظ على فئة القيمة الخاصة بها (على سبيل المثال، تظل القيم كقيم، ويتم إعادة توجيه القيم المؤقتة كقيم).
تسمح مراجع إعادة التوجيه للمرجع بالربط إما بقيمة lvalue أو rvalue اعتمادًا على النوع. تتبع إعادة توجيه المراجع قواعد المرجع المنهارة :
T& &
يصبح T&
T& &&
يصبح T&
T&& &
يصبح T&
T&& &&
يصبح T&&
خصم النوع auto
مع القيم l والقيم r:
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`
خصم معلمة نوع القالب باستخدام القيم l والقيم r:
// Since C++14 or later:
void f ( auto && t) {
// ...
}
// Since C++11 or later:
template < typename T>
void f (T&& t) {
// ...
}
int x = 0 ;
f ( 0 ); // T is int, deduces as f(int &&) => f(int&&)
f (x); // T is int&, deduces as f(int& &&) => f(int&)
int & y = x;
f (y); // T is int&, deduces as f(int& &&) => f(int&)
int && z = 0 ; // NOTE: `z` is an lvalue with type `int&&`.
f (z); // T is int&, deduces as f(int& &&) => f(int&)
f (std::move(z)); // T is int, deduces as f(int &&) => f(int&&)
راجع أيضًا: std::move
، std::forward
، rvalue references
.
يقوم ...
الجملة بإنشاء حزمة معلمات أو توسيعها. حزمة معلمات القالب هي معلمة قالب تقبل صفرًا أو أكثر من وسائط القالب (غير الأنواع أو الأنواع أو القوالب). يُطلق على القالب الذي يحتوي على حزمة معلمات واحدة على الأقل اسم القالب المتغير .
template < typename ... T>
struct arity {
constexpr static int value = sizeof ...(T);
};
static_assert (arity<>::value == 0 );
static_assert (arity< char , short , int >::value == 3 );
أحد الاستخدامات المثيرة للاهتمام لذلك هو إنشاء قائمة مُهيئ من حزمة المعلمات للتكرار عبر وسيطات الوظائف المتغيرة.
template < typename First, typename ... Args>
auto sum ( const First first, const Args... args) -> decltype(first) {
const auto values = {first, args...};
return std::accumulate (values. begin (), values. end (), First{ 0 });
}
sum ( 1 , 2 , 3 , 4 , 5 ); // 15
sum ( 1 , 2 , 3 ); // 6
sum ( 1.5 , 2.0 , 3.7 ); // 7.2
حاوية خفيفة الوزن تشبه المصفوفة من العناصر التي تم إنشاؤها باستخدام بناء جملة "القائمة ذات الأقواس". على سبيل المثال، { 1, 2, 3 }
ينشئ تسلسلات من الأعداد الصحيحة، من النوع std::initializer_list<int>
. مفيد كبديل لتمرير متجه الكائنات إلى دالة.
int sum ( const std::initializer_list< int >& list) {
int total = 0 ;
for ( auto & e : list) {
total += e;
}
return total;
}
auto list = { 1 , 2 , 3 };
sum (list); // == 6
sum ({ 1 , 2 , 3 }); // == 6
sum ({}); // == 0
التأكيدات التي يتم تقييمها في وقت الترجمة.
constexpr int x = 0 ;
constexpr int y = 1 ;
static_assert (x == y, " x != y " );
يتم استنتاج المتغيرات المكتوبة auto
بواسطة المترجم وفقًا لنوع مُهيئها.
auto a = 3.14 ; // double
auto b = 1 ; // int
auto & c = b; // int&
auto d = { 0 }; // std::initializer_list<int>
auto && e = 1 ; // int&&
auto && f = b; // int&
auto g = new auto ( 123 ); // int*
const auto h = 1 ; // const int
auto i = 1 , j = 2 , k = 3 ; // int, int, int
auto l = 1 , m = true , n = 1.61 ; // error -- `l` deduced to be int, `m` is bool
auto o; // error -- `o` requires initializer
مفيد للغاية لسهولة القراءة، خاصة للأنواع المعقدة:
std::vector< int > v = ...;
std::vector< int >::const_iterator cit = v.cbegin();
// vs.
auto cit = v.cbegin();
يمكن للوظائف أيضًا استنتاج نوع الإرجاع باستخدام auto
. في C++ 11، يجب تحديد نوع الإرجاع إما بشكل صريح، أو باستخدام decltype
كما يلي:
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2 ); // == 3
add ( 1 , 2.0 ); // == 3.0
add ( 1.5 , 1.5 ); // == 3.0
نوع الإرجاع اللاحق في المثال أعلاه هو النوع المعلن (راجع القسم الخاص بـ 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
هو عامل يقوم بإرجاع النوع المعلن للتعبير الذي تم تمريره إليه. يتم الاحتفاظ بمؤهلات السيرة الذاتية والمراجع إذا كانت جزءًا من التعبير. أمثلة على 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
الخاص بـ C. nullptr
نفسه من النوع std::nullptr_t
ويمكن تحويله ضمنيًا إلى أنواع المؤشر، وعلى عكس NULL
، غير قابل للتحويل إلى أنواع متكاملة باستثناء bool
.
void foo ( int );
void foo ( char *);
foo ( NULL ); // error -- ambiguous
foo ( nullptr ); // calls foo(char*)
التعدادات الآمنة للنوع التي تحل مجموعة متنوعة من المشاكل مع التعدادات ذات النمط C بما في ذلك: التحويلات الضمنية، وعدم القدرة على تحديد النوع الأساسي، وتلوث النطاق.
// Specifying underlying type as `unsigned int`
enum class Color : unsigned int { Red = 0xff0000 , Green = 0xff00 , Blue = 0xff };
// `Red`/`Green` in `Alert` don't conflict with `Color`
enum class Alert : bool { Red, Green };
Color c = Color::Red;
توفر السمات بناء جملة عالمي على __attribute__(...)
، __declspec
، إلخ.
// `noreturn` attribute indicates `f` doesn't return.
[[ noreturn ]] void f () {
throw " error " ;
}
التعبيرات المستمرة هي تعبيرات ربما يتم تقييمها بواسطة المترجم في وقت الترجمة. يمكن إجراء الحسابات غير المعقدة فقط في تعبير مستمر (يتم استرخاء هذه القواعد تدريجياً في الإصدارات اللاحقة). استخدم محدد constexpr
للإشارة إلى المتغير ، والوظيفة ، وما إلى ذلك هو تعبير ثابت.
constexpr int square ( int x) {
return x * x;
}
int square2 ( int x) {
return x * x;
}
int a = square( 2 ); // mov DWORD PTR [rbp-4], 4
int b = square2( 2 ); // mov edi, 2
// call square2(int)
// mov DWORD PTR [rbp-8], eax
في المقتطف السابق ، لاحظ أن الحساب عند إجراء square
يتم تنفيذه في وقت الترجمة ، ثم يتم تضمين النتيجة في توليد الكود ، بينما يتم استدعاء square2
في وقت التشغيل.
قيم constexpr
هي تلك التي يمكن للمترجم تقييمها ، ولكنها غير مضمونة ، في وقت الترجمة:
const int x = 123 ;
constexpr const int & y = x; // error -- constexpr variable `y` must be initialized by a constant expression
تعبيرات مستمرة مع الفصول:
struct Complex {
constexpr Complex ( double r, double i) : re{r}, im{i} { }
constexpr double real () { return re; }
constexpr double imag () { return im; }
private:
double re;
double im;
};
constexpr Complex I ( 0 , 1 );
يمكن للمنشئين الآن استدعاء المُنشئين الآخرين في نفس الفئة باستخدام قائمة المهيمنة.
struct Foo {
int foo;
Foo ( int foo) : foo{foo} {}
Foo () : Foo( 0 ) {}
};
Foo foo;
foo.foo; // == 0
تتيح لك الحرفيون المعرفة من قبل المستخدم تمديد اللغة وإضافة بناء الجملة الخاص بك. لإنشاء وظيفة حرفية ، حدد T operator "" X(...) { ... }
التي تُرجع نوع T
، باسم X
لاحظ أن اسم هذه الوظيفة يحدد اسم الحرفي. يتم حجز أي أسماء حرفية لا تبدأ بأحرف سطحية ولن يتم الاحتجاج بها. هناك قواعد بشأن المعلمات التي يجب أن تقبلها الوظيفة الحرفية المعرفة من قبل المستخدم ، وفقًا للنوع الحرفي المسمى.
تحويل Celsius إلى فهرنهايت:
// `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 وظائف و Lambdas بناء جملة بديلة لتحديد أنواع الإرجاع الخاصة بهم.
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 :: إنهاء.
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-qualifiers. مفيد للرمز العام والمصانع. تستخدم بالاقتران مع 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
بدلاً من استخدام المنشآت. راجع أقسام 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"
يخلق نوبة من المراجع Lvalue. مفيد لتفريغ std::pair
و std::tuple
كائنات. استخدم std::ignore
كمكتب نائب للقيم التي تم تجاهلها. في C ++ 17 ، يجب استخدام الروابط المهيكلة بدلاً من ذلك.
// With tuples...
std::string playerName;
std::tie (std::ignore, playerName, std::ignore) = std::make_tuple( 91 , " John Tavares " , " NYI " );
// With pairs...
std::string yes, no;
std::tie (yes, no) = std::make_pair( " yes " , " no " );
std::array
عبارة عن حاوية مبنية على أعلى مجموعة على غرار C. يدعم عمليات الحاويات الشائعة مثل الفرز.
std::array< int , 3 > a = { 2 , 1 , 3 };
std::sort (a.begin(), a.end()); // a == { 1, 2, 3 }
for ( int & x : a) x *= 2 ; // a == { 2, 4, 6 }
تحافظ هذه الحاويات على متوسط تعقيد الوقت الثابت للبحث وإدراج وإزالة العمليات. من أجل تحقيق تعقيد وقت ثابت ، تأمر التضحيات بالسرعة عن طريق تجزئة عناصر في دلاء. هناك أربع حاويات غير مرتبة:
unordered_set
unordered_multiset
unordered_map
unordered_multimap
std::make_shared
هي الطريقة الموصى بها لإنشاء مثيلات من std::shared_ptr
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
، ثم في المؤشر المشترك ، علينا تخصيص الذاكرة لكتلة التحكم داخل المؤشر. راجع القسم الموجود في المؤشرات الذكية لمزيد من المعلومات حول std::unique_ptr
و std::shared_ptr
.
تم استخدام std::ref(val)
لإنشاء كائن من النوع std::reference_wrapper
الذي يحمل مرجع Val. تستخدم في الحالات التي يمر فيها مرجع المعتاد باستخدام &
يتم تجميع &
يتم إسقاطه بسبب خصم النوع. std::cref
متشابه ولكن تم إنشاؤه WRIPPER المرجعي يحمل إشارة 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 ++ ، مما يعني دعم المكتبة لعمليات الترابط والعمليات الذرية. تشمل بعض هذه العمليات (ولكن لا تقتصر على) الأحمال/المتاجر الذرية ، ومقارنة و swap ، والأعلام الذرية ، والوعود ، والعقود الآجلة ، والأقفال ، ومتغيرات الحالة.
انظر الأقسام الموجودة على: std :: thread
تقوم std::async
بتشغيل الوظيفة المحددة إما بشكل غير متزامن أو تم تقييمها بشكل غير متزامن ، ثم تُرجع std::future
الذي يحمل نتيجة دعوة الوظيفة هذه.
المعلمة الأولى هي السياسة التي يمكن أن تكون:
std::launch::async | std::launch::deferred
الأمر متروك لتنفيذ ما إذا كان سيتم تنفيذ التنفيذ غير المتزامن أو تقييم كسول.std::launch::async
قم بتشغيل الكائن القابل للاتصال على مؤشر ترابط جديد.std::launch::deferred
قم بتقييم كسول على الخيط الحالي. int foo () {
/* Do something here, then return the result. */
return 1000 ;
}
auto handle = std::async(std::launch::async, foo); // create an async task
auto result = handle.get(); // wait for the result
تم إضافة وظائف std::begin
و std::end
لإرجاع التكرار وإنهاء الحاوية بشكل عام. تعمل end
الوظائف أيضًا مع المصفوفات الأولية التي لا تحتوي على وظائف الأعضاء begin
.
template < typename T>
int CountTwos ( const T& container) {
return std::count_if ( std::begin (container), std::end (container), []( int item) {
return item == 2 ;
});
}
std::vector< int > vec = { 2 , 2 , 43 , 435 , 4543 , 534 };
int arr[ 8 ] = { 2 , 43 , 45 , 435 , 32 , 32 , 32 , 32 };
auto a = CountTwos(vec); // 2
auto b = CountTwos(arr); // 1
أنتوني كالاندرا
انظر: https://github.com/anthonycalandra/modern-cpp-fueatures/graphs/contributors
معهد ماساتشوستس للتكنولوجيا