با سلام، با توجه به گزارش آنتونی پولوخین که یکی از اعضای کمیتهٔ استانداردسازی WG21 (سازمانی که توسعهٔ زبان برنامهنویسی سیپلاسپلاس را کنترل میکند). این کمیته سه بار در هر سال، هر بار در یک شهر جدید در سراسر جهان جلسه برگزار میکند. در طول این جلسات، پیشنهاداتی برای تغییر در زبان در نظر گرفته میشود. همچنین به توسعهدهندههای محلی سی++ کمک میکنند تا پیشنهادات خود را ارائه کنند. خلاصهای از جلسهٔ ماه جولای با هدف نهایی شدن استاندارد ۲۳ که نشان میهد پیشرفت بزرگی به عنوان ویژگیهای جدید استاندارد ۲۳ وجود دارد ارائه شده است:
فهرست برخی از ویژگیها به صورت زیر آمدهاست:
-
std:mdspan
-
std:flat_map
-
std:flat_set
-
freestanding
-
std:print("Hello {}", "world")
-
formatted ranges output
-
constexpr for bitset, to_chars/from_chars
-
std::string::substr() &&
-
import std;
-
std::start_lifetime_as
-
static operator()
-
[[assume(x > 0)]]
-
16- and 128-bit floats
-
std::generator
و البته ویژگیهای بسیار بیشتر از این.
ویژگی std::mdspan
از زمان اتخاذ عملگر opertator[]
چند بعدی در آخرین جلسه، معرفیstd::mdspan
به عنوان یک ویژگی سادهتر مطرح شده است و نتیجهٔ یک آرایهٔ چند بعدی غیر مالک به صورت زیر است:
using Extents = std::extents<std::size_t, 42,="" 32,="" 64="">;
double buffer[
Extents::static_extent(0)
* Extents::static_extent(1)
* Extents::static_extent(2)
];
std::mdspan<double, Extents=""> A{ buffer };
assert( 3 == A.rank() );
assert( 42 == A.extent(0) );
assert( 32 == A.extent(1) );
assert( 64 == A.extent(2) );
assert( A.size() == A.extent(0) * A.extent(1) * A.extent(2) );
assert( &A(0,0,0) == buffer );
این ویژگی حتی میتواند با سایر زبانهای برنامهنویسی خارج از جعبه کار کند. به عنوان مثال، در پارامتر الگوی سوم خود، std::mdspan
میتواند یکی از چندین کلاس طرح بندی از پیش تعریف شده را بگیرد:
-
نوع
std::layout_right
: سبک چیدمان برای C یا ++C، سطرها دارای شاخص صفر هستند.
-
نوع
std::layout_left
: سبک چیدمان برای Fortran یا Matlab، ستونها دارای شاخص صفر هستند.
شما می توانید تمام جزئیات را در سند P0009 بیابید. نویسندگان قول دادهاند که در آینده نزدیک نمونههای زیادی از std:mdspan
جدید ارائه کنند.
ویژگی std::flat_map و std::flat_set
نگهدارندههای شگفتانگیز flat_*
از کتابخانهٔ بوست، دیگر در استاندارد اصلی سی++ در دسترس هستند. این خاصیتها در کار با دادههای کم بسیار پرکاربرد هستند. در زیر ساختها، ظروف flat دادهها را در یک آرایه مرتب شده ذخیرهسازی میکنند که به طور قابل توجهی تخصیص حافظهٔ پویا را کاهش داده و موقعیت دادهها را بهبود میبخشد. علیرغم پیچیدگی جستجوی O(log N)
و پیچیدگی درجO(N)
در بدترین حالت، ظروف مسطح هنگام کار با مقدار کمی از عناصر بهتر از std:unordered_map
عمل میکنند. در واقع، در طی فرآیند استانداردسازی، ظروف flat_*
به عنوان آداپتور ساخته شدهاند. به این ترتیب، برنامهنویسان میتوانند از نگهدارندههای خود برای پیادهسازی اساسی استفاده کنند:
template <std::size_t N>
using MyMap = std::flat_map<
std::string, int, std::less<>,
mylib::stack_vector<std::string, N>, mylib::stack_vector<int, N>
>;
static MyMap<3> kCoolestyMapping = {
{"C", -200},
{"userver", -273},
{"C++", -273},
};
assert( kCoolestyMapping["userver"] == -273 );
const auto& keys = kCoolestyMapping.keys(); // Inspired by Python :)
assert( keys.back() == "userver" );
یک نکتهٔ جالب این است که استاندارد STL برخلاف پیادهسازی Boost، کلیدها و مقادیر را در نگهدارندهها جداگانه ذخیره میکند. این مکانِ کلیدیِ بهبود یافته، جستجوی ظرفِ flat را سریعتر میکند.
رابط کاملstd::flat_set
در سند P1222 توضیح داده شده است، در حالی که شرح رابط std:flat_map
در سند P0429 موجود است.
مستقل (Freestanding)
استاندارد ++C میگوید که امکان پیادهسازی کتابخانهٔ استاندارد به صورت میزبان (hosted) یا مستقل (freestanding) وجود دارد. پیادهسازی میزبان نیاز به پشتیبانی سیستمعامل دارد و باید تمام روشها و کلاسها را از کتابخانهٔ استاندارد پیادهسازی کند. مستقل (freestanding) میتواند بدون سیستمعامل کار کند، سختافزار مهم نیست، و برخی از کلاسها و توابع را شامل نمیشود. تا همین اواخر، هیچ توضیحی برای ایستادن آزاد وجود نداشت و سازندگان سختافزارهای مختلف بخشهای مختلفی از کتابخانهٔ استاندارد را ارائه میکردند. این کارِ پورت کردن کد را سختتر کرد و محبوبیت ++C را در محیطهای تعبیهشده (امبدها) تضعیف کرد.
بنابراین، زمان تغییر آن فرا رسیده است! سند P1642 مشخص کرده است که کدام بخش از کتابخانهٔ استاندارد برای freestanding اجباری است.
ویژگی std::print
روشهایی از کتابخانهء محبوب fmt در C++20 اضافه شد. این کتابخانه آنقدر راحت و سریع بود که برنامهنویسان شروع به استفاده از آن کرده و تقریباً در همهجای کد خود به کار بردهاند، از جمله برای خروجی قالببندی شده:
std::cout << std::format(“Hello, {}! You have {} mails”, username, email_count);
اما کدی مانند آن به دلایل زیر کامل نیست:
-
تخصیص پویا اضافی.
-
نیاز به
std::cout
جهت قالببندی خطوط از قبل قالب بندی شده.
-
عدم پشتیبانی از یونیکد.
-
کدی که اندازهٔ فایل باینری حاصل را افزایش میدهد.
-
ظاهری نه چندان جذاب.
بنابراین، تمام این مشکلات با اضافه کردن متدهایstd::print
حل شد:
std::print(“سلام, {}! به جامعهٔ {} خوش آمدید!”, name, community);
میتوانید جزئیات، معیارها و گزینههای استفاده ازstd::print
باFILE*
و استریمها را در سند P2093 بیابید.
خروجی قالببندی شده محدودههای مقدار
به لطف سند P2286 و، std::format
(و std::print
) اکنون میتوانند محدودههایی از مقادیر را بدون در نظر گرفتن اینکه در یک ظرف هستند یا توسط std::ranges::views::*
ارائه شدهاند خروجی بگیرند.
std::print("{}", std::vector<int>{1, 2, 3}); // Output: [1, 2, 3]
std::print("{}", std::set<int>{1, 2, 3}); // Output: {1, 2, 3}
std::print("{}", std::pair{42, 16}); // Output: (42, 16)
std::vector v1 = {1, 2};
std::vector v2 = {'a', 'b', 'c'};
auto val = std::format("{}", std::views::zip(v1, v2)); // [(1, 'a'), (2, 'b')]
ویژگی constexpr
اخبار تجزیه و تحلیل عالی برای توسعهدهندگانی که با کتابخانههای مختلف کار میکنند وجود دارد:
خاصیتهایstd::to_chars/std::from_chars
اکنون میتوانند در مرحله کامپایل برای تبدیل مقادیر صحیح از متن به باینری استفاده شوند. این نیز باید هنگام توسعه DSL مفید باشد. به نظر میرسد توسعهدهندههای روسی Yandex Go (به نقل از عضو کمیته) قصد دارند از آن در چارچوب کاربر برای بررسی پرس و جوهای SQL در مرحله کامپایل استفاده کنند. گزینهٔ std::bitset
نیز تبدیل به constexpr
شده است، بنابراین کار با بیتها در مرحلهٔ کامپایل اکنون بسیار آسانتر از قبل است. دانیل گوچاروف روی std::bitset
در سند P2417 کار کرد و الکساندر کارائف در سند std::to_chars/std::from_chars
P2291 به او پیوست. با تشکر فراوان از آنها برای این کار خوب انجام شده!
ویژگی import std;
با توجه به اینکه، اولین ماژول کامل(تمامعیار) به کتابخانهٔ استاندارد (STL) اضافه شد. اکنون میتوان کل کتابخانه را با یک خط بر سند وارد کرد: import std;
. اگر کل ماژول کتابخانهٔ استاندارد به جای گنجاندن فایلهای هدر وارد شود، ساختها میتوانند تا ۱۱ برابر (گاهی اوقات حتی ۴۰ بار!) سریعتر شوند. میتوانید بنچمارک ها را در P2412 مشاهده کنید. اگر به ترکیب ++C و C و همچنین استفاده از توابع C از فضای نام جهانی عادت دارید، ماژول std.compat
برای شما مناسب است. وارد کردن آن همهٔ توابع فایلهای سرآیند C مانند ::fopen
و ::isblank
و همچنین محتویات کتابخانهٔ استاندارد را در اختیار شما قرار میدهد.
با وجود همهٔ اینها، سند P2465 که ماژولهای جدید را پوشش میدهد، در واقع آنقدرها هم طولانی نیست.
ویژگی std::start_lifetime_as
تیمور داملر و ریچارد اسمیت یک هدیهٔ فوقالعاده برای همهٔ توسعهدهندگانی که روی برنامههای تعبیه شده (امبد) و پربار کار میکنند گرد هم آوردهاند. اکنون تنها چیزی که برای کار کردن همه چیز نیاز دارید این است:
struct ProtocolHeader {
unsigned char version;
unsigned char msg_type;
unsigned char chunks_count;
};
void ReceiveData(std::span<std::byte> data_from_net) {
if (data_from_net.size() < sizeof(ProtocolHeader)) throw SomeException();
const auto* header = std::start_lifetime_as<ProtocolHeader>(
data_from_net.data()
);
switch (header->type) {>
// ...
}
}
به عبارت دیگر، میتوانید بافرهای مختلف را به ساختارها تبدیل کنید و با آنها بدون reinterpret_cast
، کپی کردن دادهها یا خطر عملکرد برنامهتان کار کنید. همه چیز در سند P2590 شرح و مستند شده است.
ویژگیهای شناورهای (اعشاری) 16 و 128 بیتی
استاندارد ++C اکنون شامل std::float16_t
، std::bfloat16_t
، std::float128_t
و نام مستعار برای اعداد موجود با ممیز شناور است: std::float32_t
، std::float16_t
. شناورهای 16 بیتی در هنگام کار با کارتهای ویدئویی یا یادگیری ماشین کمک میکنند. به عنوان مثال، float16.h
میتواند از انواع جدید شناور کوتاه بهرهمند شود. شناورهای 128 بیتی برای محاسبات علمی شامل اعداد بزرگ بهترین هستند. سندِ P1467 ماکروها را برای بررسی پشتیبانی کامپایلر برای اعداد جدید توصیف میکند، و حتی خاصیتِ stdfloat.properties
، در جدول مقایسه با توصیف اندازههای مانتیس و توان در بیتها وجود دارد.
ویژگی std::generator
زمانی که کروتینها در استاندارد C++20 پذیرفته شدند، ایده این بود که میتوان از آنها برای ایجاد «مولد» استفاده کرد: توابعی که وضعیت خود را بین تماسها به خاطر میآورد و مقادیر جدید را بر اساس آن حالت برمیگرداند. در استاندارد C++23 با اشاره به، std::generator
به عنوان یک کلاس جدید یاد میشود که به شما امکان میدهد به راحتی ژنراتورهای خود را ایجاد کنید:
std::generator<int> fib() {
auto a = 0, b = 1;
while (true) {
co_yield std::exchange(a, std::exchange(b, a + b));
}
}
int answer_to_the_universe() {
auto rng = fib() | std::views::drop(6) | std::views::take(3);
return std::ranges::fold_left(std::move(rng), 0, std::plus{});
}
در مثال فوق میتوانید ببینید که ژنراتورها با std::ranges
چقدر خوب کار میکنند. std::generator
کارآمد و ایمن است. کدی که به نظر میرسد یک پیوند معلق ایجاد میکند در واقع کاملاً معتبر است و هیچ مشکلی ایجاد نمیکند:
std::generator<const std::string&=""> greeter() {
std::size_t i = 0;
while (true) {
co_await promise::yield_value("hello" + std::to_string(++i)); // Everything is ok!
}
}
میتوانید مثالها و توضیحاتی دربارهٔ نحوه کارکرد و استدلال پشت این رابط را در سند P2502 بیابید.
سورپرایزهای دلپذیر
کلاس string
استاندارد برای متد substr()
برای ارجاعات rvalue
یک بازنگری اساسی (بهبود) دریافت کرده است: std::string::substr() &&
. مانند مثال زیر:
std::string StripSchema(std::string url) {
if (url.starts_with("http://")) return std::move(url).substr(5);
if (url.starts_with("https://")) return std::move(url).substr(6);
return url;
}
این روش اکنون بدون تخصیص پویا اضافی کار میکند. اطلاعات بیشتر را میتوانید در سند P2438 بیابید.
به لطف سند P1169، اکنون میتوانیدoperator()
را ثابت اعلام کنید، که برای ایجاد CPO برای محدودهها در کتابخانه استاندارد عالی است:
namespace detail {
struct begin_cpo {
template <typename T>
requires is_array_v<remove_reference_t<T>>
|| member_begin<T> || adl_begin<T>
static auto operator()(T&& val);
};
void begin() = delete; // poison pill
} // namespace detail
namespace ranges {
inline constexpr detail::begin_cpo begin{}; // ranges::begin(container)
} // namespace ranges
علاوه بر std::start_lifetime_as
، تیمور داملر یک راهنمایی عالی برای بهینهساز ارائه کرد[[assume (x > 0)]]
. اکنون میتوانید در مورد مقادیر احتمالی اعداد و سایر متغیرهای ثابت به کامپایلر نکاتی بدهید. برخی از مثالها و معیارها در سند P1774 کاهش پنج برابری در تعداد دستورالعملهای اسمبلی را نشان میدهند.
این استاندارد همچنین دارای بسیاری از ویرایشهای جزئی، رفع اشکال و پیشرفتها بوده است، در اینجا منظور استاندارد ۲۳ است. در برخی مکانها، از سازندههای حرکتی (move constructors) به جای سازندههای کپی (copy constructors) استفاده شد (P2266). خوشبختانه برای توسعهدهندگان درایور، برخی از عملیات فرار دیگر منسوخ نمیشوند (P2327 با رفع اشکال در C++20). عملگر<=>
کدهای قدیمی را کمتر میشکند (P2468)، کاراکترهای یونیکد اکنون میتوانند با نام استفاده شوند (P2071)، و کامپایلرها عموماً برای پشتیبانی از یونیکد (P2295) مورد نیاز هستند. الگوریتمهای جدید برای محدودهها (ranges::contains
P2302, views::as_rvalue P2446, views::repeat
P2474, views::stride
P1899, و ranges::fold
P2322) و std::format_string
برای بررسیهای زمان کامپایل اضافه شد. std::format
(P2508) و ماکروی #warning
در (P2437). محدودهها (Ranges) یاد گرفتاند که چگونه با انواع فقط حرکت کار کنند (P2494). و در نهایت std::forward_like
برای ارسال متغیرها بر اساس نوع متغیر دیگری اضافه شد (P2445).
برای مدت طولانی، به نظر میرسید مهمترین نوآوری C++23 اضافه کردن std::stacktrace
از RG21 بود، اگرچه در آخرین جلسه ویژگیهای مورد انتظار بسیاری اضافه شد. نوآوریهایی برای توسعهدهندگان تعبیه شده، شیمیدانان/فیزیکدانان/ریاضیدانان/...، توسعهدهندگان کتابخانههای یادگیری ماشین، و حتی توسعهدهندگانی که روی برنامههای کاربردی با بار بالا کار میکنند، وجود دارد.
نقل قول
در ادامهٔ این مقاله در نظر داریم به بهروز رسانیهای جدید از استاندراد ۲۶ اشاره کنیم.