رفتن به مطلب
جامعهٔ برنامه‌نویسان مُدرن ایران
کامبیز اسدزاده

قابلیت‌های ممتاز و پیشرفتهٔ ++C در استاندارد‌های جدید


کامبیز اسدزاده

اگر به دنبال یادگیری ویژگی‌های جدید از استاندارد سی++ هستید، این مقاله را به شما توصیه می‌کنم. در این مقاله من قصد دارم به ویژگی‌های حذف شده، اضافه شده، بهبود‌یافته و در حال توسعه از استاندارد‌های ۱۱ تا به بعد اشاره کنم. به خصوص استاندارد ۲۰ یا همان 2a هدف بعدی من است.

پیغام توسط کامبیز اسدزاده افزوده شد

پست های پیشنهاد شده

اگر شما توسعه دهنده‌ٔ ++C هستید، توصیه می‌کنم این سری از مقالات را دنبال کنید زیرا در این تاپیک قصد دارم به چکیده‌ای از آخرین تغییرات مرتبط با سی‌پلاس‌پلاس پیشرفته اشاره کنم. بنابراین در بخش اول، مهم‌ترین موارد منسوخ شده، اشکلات رفع شده و ویژگی‌های جدید در استاندارد‌های اخیر را پوشش خواهیم داد‌ که به صورت جزئی خواهد بود و سپس نسبت به هر کدام در مقالات جداگانه به کاربرد‌های پیشرفته‌تر و جزئیات بیشتری اشاره خواهیم کرد.

new-cpp-01.png

نقل قول

این مقاله شامل توضیحات مربوط به هستهٔ زبان است، درواقع به ویژگی‌های خودِ زبان اشاره می‌شود نه به اضافات و امکانات جدید از کتابخانهٔ STL. بنابراین در یک مقالهٔ جداگانه به ویژگی‌های جدید کتابخانه‌ٔ استاندارد اشاره خواهد شد که به زودی در این مکان لینک می‌شود.

قبل از شروع، اگر می‌خواهید به لیستی از تغییرات و ویژگی‌های کامل در استاندارد‌ها دسترسی داشته باشید به مقالهٔ زیر مراجعه کنید. در مقالهٔ فوق به لیست ویژگی‌های جدید در استاندارد ۱۱، ۱۴، ۱۷ و ۲۰ اشاره شده است.

جزئیات ++C نسخه ۱۷ (بهبود‌ها و تغییرات)

بیایید به آرامی شروع کنیم، امروز ما به عناصر حذف شده و یا به موارد بهبود یافتهٔ کتابخانه استاندارد بپردازیم.

معرفی به صورت سلسله مراتبی

  • عناصر حذف شده و توسعه یافته (در این بحث)
  • شفاف سازی در زبان
  • قالب‌ها
  • ویژگی‌ها
  • تغییرات اول کتابخانه 
  • تغییرات دوم کتابخانه

مستندات و لینک‌ها

  1. قبل از هر چیز، اگر شما خودتان می‌خواهید استاندارد جدید را کاوش کنید آخرین پیش نویسه را در این بخش مطالعه کنید.
  2. در صورتی که می‌خواهید بدانید کدام کامپایلر از ویژگی‌های جدید پشتیبانی می‌کند، در این بخش آن را پیگیری کنید.
  3. علاوه بر این، لیستی از توصیف‌های مختصر از تمامی ویژگی‌های زبان سی‌پلاس‌پلاس ۱۷ تهیه شده است که در این بخش می‌توانید آن را ببینید که در قالب PDF از طرف مرجع رسمی می‌باشد.

مواردی که ترجیح داده شده است که حذف شوند


حذف تریگراف
تریگراف‌ها کاراکترهای ویژه ترتیبی هستند که در موقع عدم پشتیبانی سیستم از نوع ۷ بیتی اَسکی (ASCII) همانند ایزو 646 استفاه شوند. برای مثال =?? کاراکتر ویژه‌ای مانند # تولید شده را در قالب -?? تولید می‌کند. تمامی مجموعه کاراکترهای اصلی سی‌پلاس‌پلاس در قالب 7 بیتی اسکی قرار دارند. موضوع فوق به ندرت مورد استفاده قرار می‌گیرد، بنابراین حذف آن ممکن است به ترجمه ساده کد کمک کند.

اگر شما می‌خواهید اطلاعات بیشتری در رابطه با کارآیی تیرگراف‌ها در سی++ کسب کنید به این لینک مراجعه کنید.

----------------------------------------------------------------------------
| trigraph | replacement | trigraph | replacement | trigraph | replacement |
----------------------------------------------------------------------------
| ??=      | #           | ??(      | [           | ??<      | {           |
| ??/      | \           | ??)      | ]           | ??>      | }           |
| ??’      | ˆ           | ??!      | |           | ??-      | ˜           |
----------------------------------------------------------------------------

شما جزئیات بیشتر را می‌توانید در سند N4086 بیابید.

اگر شما واقعاً به هر نحوی به گراف‌ها در ویژوال استودیو نیاز دارید، نگاهی به مشخصه /Zc:trigraphs در بخش پیکربندی داشته باشید. همچنین، کامپایلرهای دیگر ممکن است مواردی را پشتیبانی نکنند. وضعیت انجام شده کنونی در کامپایلر های GCC:5.1 و Clang:3.5 می‌باشد.

 

حذف کلمه کلیدی register
کلمه کلیدی register در استاندارد 2011 سی‌پلاس‌پلاس منسوخ شده است و دیگر استفاده از آن معنایی ندارد. این کلمه کلیدی در حال حاضر حذف شده است. این کلمه کلیدی محفوظ است و ممکن است در نسخه های بعدی باز نویسی شود (مثلا autokeyword به عنوان یک چیز قدرتمند مجددا مورد استفاده قرار گرفته است).

جزئیات بیشتر در رابطه با این مورد در P0001R1 قابل مشاهده است. البته فعلا در MSVC انجام نشده است اما در کامپایلر‌های GCC 7.0 و Clang 3.8 انجام شده است.

 

حذف Operator++ bool
این اپراتور برای زمان بسیار زیادی است که منسوخ شده است! در سی پلاس پلاس ۹۸ تصمیم بر آن گرفته بودند که از آن استفاده کنند اما در نسخه ۱۷ سی‌پلاس‌پلاس کمیته موافقت خود را جهت حذف آن از زبان اعلام کرده است.

جزئیات بیشتر در رابطه با این مورد در P0002R1 قابل مشاهده است. البته فعلا در MSVC انجام نشده است اما در کامپایلر‌های GCC 7.0 و Clang 3.8 انجام شده است.

 

حذف مشخصات استثنایی از استاندارد ۱۷
در سی پلاس پلاس ۱۷، مشخصات استثنایی بخشی از نوع سیستمی خواهند بود (به P0012R1 نگاه کنید). با این حال، استاندارد شامل مشخصات استثنایی قدیمی و منسوخ شده اند که به نظر غیرعلمی و غیرقابل استفاده است.

void fooThrowsInt(int a) throw(int) { 
  printf_s("can throw ints\n"); 
  if (a == 0) 
    throw 1; 
}

کد بالا در سی‌پلاس‌پلاس ۱۱ رد (منسوخ شده است). تنها اعلامیه استثنایی علمی throw() است، به این معنی است که این کد چیزی را در قالب throw انجام نخواهد داد. اما از سی‌پلاس‌پلاس ۱۱ به اینور، برنامه نویسان توصیه کرده اند که کسی از آن استفاده نکند.

برای مثال در کامپایلر Clang 4.0 شما باید خطای زیر را دریافت کنید:

error: ISO C++1z does not allow dynamic exception specifications [-Wdynamic-exception-spec]
note: use 'noexcept(false)' instead

جزئیات بیشتر در رابطه با این مورد در P0003R5 قابل مشاهده است. البته فعلا در MSVC انجام نشده است اما در کامپایلر‌های GCC 7.0 و Clang 3.8 انجام شده است.

 

حذف auto_ptr
این یکی از به روز رسانی‌های خوبی است که در سی‌پلاس‌پلاس ۱۱، ما اشاره گرهای هوشمند را دریافت کردیم : unique_ptr,shared_ptr و weak_ptr. با تشکر از این حرکتی که کمیته انجام داده بود، معنای واقعی این به روز رسانی در این بود که زبان می‌تواند پشتیبانی مناسبی از انتقال منابع منحصربفرد را داشته باشد. در این میان auto_ptr یک چیز قدیمی و نادرست در زبان بود به نا به دلایلی auto_ptr در این جا منسوخ شده است و باید به صورت خودکار به unique_ptr تبدیل شود.

توجه داشته باشیم که auto_ptr مدت کوتاهی است که از سی‌پلاس‌پلاس ۱۱ به اینور منسوخ شده است و بسیاری از کامپایلر ها منسوخ شدن آن را گزارش می‌دهند که به صورت زیر خواهد بود:

warning: 'template<class> class std::auto_ptr' is deprecated

در حال حاضر آن به وضعیت نامناسب تبدیل شده است، و اساساً کد شما کامپایل نخواهد شد. در اینجا خطا از طرف MSVC 2017 زمانی که از گزینه /std::c++latest استفاده کنید اعلام خواهد شد.

error C2039: 'auto_ptr': is not a member of 'std'

اگر شما نیاز به کمک از تبدیل از auto_ptr به unique_ptr دارید، می‌توانید Clang Tidy را بررسی کنید، زیرا آن عمل تبدیل خودکار را انجام خواهد داد. اطلاعات بیشتر در سند N4190 موجود است.

همچنین موارد مرتبط دیگری با سند N4190 وجود دارند که در کتابخانه خذف شده اند مانند:

  • unary_function/binary_function
  • ptr_fun()
  • mem_fun()/mem_fun_ref()
  • bind1st()/bind2nd()
  • random_shuffle

قوانین جدید خودکار برای Direct-List-Initialization از سی پلاس پلاس ۱۱ به اینور که ما یک مشکل بزرگی در این رابطه داشتیم:

auto x { 1 };

از initializer_list اینطور نتیجه‌گیری شده است. با استاندارد جدید، ما می‌توانیم این مشکل را حل کنیم. بنابراین آن می‌تواند به عنوان نوع int که اکثر مردم تصور می‌کنند شناسایی شود. برای اینکه این اتفاق بیافتد، ما نیاز داریم که دو روش تخصیص مقدار اولیه را درک کنیم: کپی و مستقیم.

  • auto x = foo(); // copy-initialization
  • auto x{foo}; // direct-initialization, initializes an
  • // initializer_list (until C++17)
  • int x = foo(); // copy-initialization
  • int x{foo}; // direct-initialization

برای مقدار دهی اولیه، سی‌پلاس‌پلاس ۱۷ قوانین جدیدی را معرفی می‌کند:

  • For a braced-init-list with only a single element, auto
  • deduction will deduce from that entry;
  • For a braced-init-list with more than one element, auto
  • deduction will be ill-formed.

برای مثال:

auto x1 = { 1, 2 };   // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type
auto x3{ 1, 2 };      // error: not a single element
auto x4 = { 3 };      // decltype(x4) is std::initializer_list<int>
auto x5{ 3 };         // decltype(x5) is int

جزئیات بیشتر را در سند N3922 می‌توانید مشاهده کنید. همچنین جزئیات در رابطه با فهرست خودکار موجود هستند که توسط جناب آقای Ville Voutilainen اشاره شده است. این اضافات در سی‌پلاس‌پلاس از زمان MSVC 14.0، GCC 5.0 و Clang 3.8 کار می‌کنند.

 

گزینه static_assert بدون هیچ نوع پیغامی
این واضح است که، این به شما این امکان را می دهد که فقط بدون داشتن گذراندن پیام، نسخه دارای پیغام در دسترس خواهد بود. این سازگاری با سایر موارد مانند BOOST_STATIC_ASSERT وجود دارد.

static_assert(std::is_arithmetic_v<T>, "T must be arithmetic");
static_assert(std::is_arithmetic_v<T>); // no message needed since C++17

جزئیات بیشتر در سند N3928 در دسترس است. پشتیبانی شده در MSVC 2017 ٬ GCC 6.0 و Clang 2.5.

 

انواع مختلف شروع و پایان در محدوده حلقه
از سی‌پلاس‌پلاس ۱۱ به بعد، محدوده مبتنی بر حلقه ها به صورت داخلی تعریف شده است:

{
auto && __range = for-range-initializer;
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
  for-range-declaration = *__begin;
  statement
}
}

همانطور که می‌بینید، __begin و __end دارای نوع مشابه هستند. این ممکن است باعث مشکلاتی شود. برای مثال زمانی که شما چیزی شبیه یک نگهبان (محافظ) که از نوع داده دیگری است را داشته باشید مشکل ساز خواهد بود.

در سی‌پلاس‌پلاس ۱۷ آن به صورت زیر تغییر کرده است:

{
auto && __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr;
for ( ; __begin != __end; ++__begin ) {
  for-range-declaration = *__begin;
  statement
}
}

انواع __begin و __end ممکن است متفاوت باشد چرا که فقط اپراتور مقایسه مورد نیاز است. این تغییر کلی باعث می‌شود که این ویژگی تجربه بیشتری را در این زمینه برای کاربران ارائه دهند. جزئیات بیشتر در P0184R0، پشتیبانی شده در MSVC 2017 ،GCC 6.0 و Clang 3.6.

  • پسندیدن 1
  • تشکر شده 2

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

جزئیات در ۱۱ ++C و برخی بهبود‌ها در ویرایش ۱۷

جدا کننده رقم استاندارد ۱۱

تا قبل از استاندارد ۱۴ شما مجبور بودید تعداد رقم‌ها و یا صفر‌ها را بشمارید. از استاندارد ۱۴ به بعد دیگر نیازی به این کار نیست. این کار در زمان محاسبهٔ آدرس در کلمه، مرز‌های رقمی یا نیم کاراکتر‌ها مفید خواهد بود. در واقع با گروه‌بندی ارقام کد شما رسا‌تر خواهد شد.

int no = 1'000'000;
long addr = 0xA000'EFFF;
uint32_t binary = 0b0001'0010'0111'1111;

تعریف نام مستعار (Type aliases) استاندارد ۱۱

از لحاظ معنایی مشابه typedef است، هرچند نام‌های مستعار تعریف شده به این شیوه ساده‌تر بوده و تحت انواع الگو‌ها سازگار‌تر هستند. به مثال‌های زیر توجه کنید:

template <typename T>
using dynamicArray = std::vector<T>;
dynamicArray<int> nums; // equivalent to std::vector<int>
using func_ptr = int (*)(int);

و یا اگر بخواهید نام مستعاری از یک نوع std::map با کلید و مقدار از نوع رشته تعریف کنید، آنگاه به صورت زیر خواهد بود:

using mapString  = std::map<std::string, std::string>;

این روش برای موارد متنوعی مورد استفاده قرار که قبل و بعد از وجود آن را در مثال زیر می‌بینیم:

// C++11
using counter = long;

// C++03 equivalent:
// typedef long counter;

// C++11
using fmtfl = std::ios_base::fmtflags;

// C++03 equivalent:
// typedef std::ios_base::fmtflags fmtfl;

fmtfl fl_orig = std::cout.flags();
fmtfl fl_hex = (fl_orig & ~std::cout.basefield) | std::cout.showbase | std::cout.hex;
// ...
std::cout.flags(fl_hex);

// C++11
using func = void(*)(int);

// C++03 equivalent:
// typedef void (*func)(int);

// func can be assigned to a function pointer value
void actual_function(int arg) { /* some code */ }
func fptr = &actual_function;

template<typename T> using ptr = T*;

// the name 'ptr<T>' is now an alias for pointer to T
ptr<int> ptr_int;

الفاظ رشته‌ای تعریف شده توسط کاربر (User-defined literals) استاندارد ۱۱

  • در مواقع نیاز شما مجبورید با اصطلاحات واضحی مثل MB, KB, GB یا km، cm و یا واحد‌های دیگری جهت تبدیلات در زمان اجرا کار‌ی انجام دهید. اکنون به عنوان کاربر می‌توانید اصلاح مورد نیاز خود را همانند انواع pt (انواع ابتدائی) دیگر انجام دهید.
  • این شیوه برای واحد‌ها و اندازه‌گیری بسیار مناسب است.
  • اضافه کردن constexpr اثر هزینهٔ صفر از کارایی را در زمان اجرا فراهم می‌کند.
using ull = unsigned long long;

constexpr ull operator"" _KB(ull no)
{
    return no * 1024;
}
constexpr ull operator"" _MB(ull no)
{
    return no * (1024_KB);
}

int main()
{
  
  std::cout << 1_KB << std::endl;
  std::cout << 1_MB << std::endl;
  
  return 0;
}

ویژگی مقداردهی لیست اولیه (std::initializer_list) استاندارد ۱۱

std::pair<int, int> p = {1, 2};
std::tuple<int, int> t = {1, 2};
std::vector<int> v = {1, 2, 3, 4, 5};
std::set<int> s = {1, 2, 3, 4, 5};
std::list<int> l = {1, 2, 3, 4, 5};
std::deque<int> d = {1, 2, 3, 4, 5};
std::array<int, 5> a = {1, 2, 3, 4, 5};

این شیوه مشابه آرایه‌ها به سبک C مقادیر را مستقیماً با لیست مقدار دهندهٔ اولیه «std::initializer_list» اختصاص می‌دهد. همچنین به لطف استاندارد ۱۱ این مورد در مورد نگه‌دارنده‌های تو در تو هم صدق می‌کند.

 

ویژگی auto و decltype استاندارد ۱۱، بهبود و اضافات در استاندارد ۲۰

  • متغیر‌های تعریف شده به وسیلهٔ کلمهٔ کلیدی auto بر اساس نوع مقدار توسط کامپایلر استنباط می‌شوند.
  • متغیر‌های تعرف شده به کمک کلمهٔ کلیدی decltype بر اساس بررسی موجودیت یا نوع و مقادیر عبارت تعیین می‌شوند.
  • این ویژگی‌ها به شدت برای خواندن، به ویژه انواع پیچیده مفید است.
auto a = 3.14; // double

اجازه دهید یک مثال با ترکیب auto و decltype داشته باشیم.

به عنوان مثال، توابع می‌توانند به وسیلهٔ کلمهٔ کلیدی auto نیز نوع بازگشتی خود را استنباط کنند. در استاندارد ۱۱ نوع بازگشتی باید به طور صریح یا با استفاده از نوع decltype رمزگشایی شود، به مثال‌های عادی و الگو (template) مانند زیر توجه کنید:

auto getResult(std::string var) -> decltype (var) {
  return var;
}
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

ویژگی Range-based for-loops استاندارد ۱۱ به علاوهٔ اضافات و بهبود‌ها در استاندارد ۱۷ و ۲۰

یک نحوِ شیرین برای تکرار عناصر در یک نگه‌دارنده یا ظرف (container)

std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (const int& i : v) // access by const reference
  std::cout << i << ' ';
  std::cout << '\n';

برای بررسی برخی از ویژگی‌های مرتبط با این موضوع به مقالات زیر مراجعه کنید:

 

اشاره‌گر‌های هوشمند استاندارد ۱۱، بهبود‌ها در استاندارد ۱۷

استاندارد ۱۱ با خود اشاره‌گر‌های هوشمند را معرفی کرده است مانند std::unique_ptr، std::shared_ptr و std::weak_ptr که سپس std::auto_ptr در استاندارد ۱۷ به عنوان منسوخ شده معرفی و حذف شده است.

std::unique_ptr<int> p = std::make_unique<int>(128);

 برای آشنایی با این ویژگی، لازم است در رابطه با اصطلاح RAII آشنا شوید، برای این منظور مقالهٔ زیر را برای مطالعه پیشنهاد می‌کنم.

 

 

صفت‌های استاندارد جدید مانند، [[fallthrough]]، [[maybe_unused]] و [[nodiscard]] استاندارد ۱۷

این بحث به خودی خود بسیار جذاب است، بنابراین برای آن یک مقالهٔ ویژه در نظر گرفته‌ام که در ادامه می‌توانید به آن مراجعه کنید.

 

ویژگی فضاهای نام تو در تو (Nested namespace) استاندارد ۱۷

فضاهای نام یکی از کاربردی‌ترین ویژگی‌های سی++ از زمان استاندارد ۱۱ به بعد می‌باشد که نحوهٔ تعریف آن به صورت زیر است:

namespace Base {
  namespace Person {
    class PersonInfo {
    public:
      std::string name = {"Kambiz"};
    };
  }
}

در ادامهٔ استاندارد ۱۷ بهبود آن به شیوهٔ تو در تو صورت گرفته است و می‌تواند به صورت زیر تعریف شود:

namespace Base::Person {
    class PersonInfo {
    public:
      std::string name = {"Kambiz"};
    };
}

در نهایت دسترسی به فضای نام و اعضای آن به همان شیوهٔ ساده ممکن است:

Base::Person::PersonInfo pInfo;
  std::cout << pInfo.name << std::endl;

 

یک کاراکتر u8 لفظی استاندارد ۱۷

این ویژگی برای ترجمهٔ صحیح کاراکتر‌ها به اسکی (ASCII) در پلتفرم‌های مختلف استفاده می‌شود.

auto c = {u8'C'};

و یا به صورت یک رشتهٔ کامل:

auto str = u8"سلام";
  std::cout << str << "/n";

 

امکان استفاده از auto در template  استاندارد ۱۷

این ویژگی امکان این را می‌دهد تا نوع پارامتر‌ها را در الگو‌ها تعریف کنیم، در استاندارد ۱۴ این ویژگی به صورت زیر ممکن است:

template<typename Type, Type x> constexpr auto value = x;
int main()
{   
   auto x = value<int,3>;
   return 0;
}

و اینک به لطف استاندارد ۱۷ استفاده از کلمهٔ کلیدی auto نیز فراهم شده است که به طور زیر تعریف می‌شود:

template<auto x> constexpr auto value = x;
 
int main()
{   
   auto x = value<3>;
 
   return 0;
}

نوع متغیر x توسط کامپایلر استنباط می‌شود، البته باید در نظر داشت این ویژگی در استفاده از variadic templates هم کاربرد دارد. به مثال زیر توجه کنید:

template<auto ... values> struct A {};
 
int main()
{   
   auto k = A<1, 2, 'c'>();
   return 0;
}

الفاظ رشته‌ای (لیترال) با ممیز شناور‌ (Floating point) در مبنای هگزادسیمال استاندارد ۱۷

در استاندارد ۱۷ ممیز شناور در مبنای هگزادسیمال اضافه شده است. اگر یک حرف شناور لفظی، آغازش با دنبالهٔ کاراکتر‌های 0x یا 0X باشد، آن در مبنای هگزا دسیمال شناور محسوب می‌شود. در غیر این صورت در مبنای شناور دسیمال (مبنای ۱۰) تعریف می‌شود.

0x | 0X hex-digit-sequence exponent suffix
double d = 0x1.2p3; // hex fraction 1.2 (decimal 1.125) scaled by 2^3, that is 9.0

به مثال زیر توجه کنید:

#include <iostream>
int main()
{
  std::cout << 58.         << '\n'
            << 4e2         << '\n'
            << 123.456e-67 << '\n'
            << .1E4f       << '\n'
            << 0x10.1p0    << '\n';
}

نتیجهٔ تحت این ویژگی به این صورت است:

58
400
1.23456e-65
1000
16.0625

این مقاله ادامه دارد...

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
مهمان
این موضوع برای عدم ارسال قفل گردیده است.

  • کاربران آنلاین در این صفحه   0 کاربر

    هیچ کاربر عضوی،در حال مشاهده این صفحه نیست.

×
×
  • جدید...