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

برخی از ویژگی‌های C++17 برای ساده و تمیز نویسی کُد


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

با سلام،

در این پست من قصد دارم به چند ویژگی استاندارد 1z اشاره کنم که به شما اجازه میده تا کُد تمیزتر، ساده‌تر و خواناتری را ایجاد کنید. توسعه زبان‌های برنامه‌نویسی روز به روز بیشتر شده و سی++ به عنوان یک زبان پیچیده نیاز به این داره تا کاربران رو از لحاظ سادگی و مدرنیزه شدن سینتکس دلگرم کنه.

در استاندارد جدید ۱۷ من برخی از ویژگی‌ها رو معرفی می‌کنم که در تمیز نوشتن و ساده نوشتن تاثیر بسیاری دارند.

ویژگی ساختار‌های پیوندی

این ویژگی یکی از ویژگی‌های جدید سی++ است که امکان پیوند شدن نام‌های مشخص و زیر اشیاء المنت‌های اولیه را می‌دهد. به عبارت ساده‌تر می‌توان گفت که، ساختار‌های پیوندی (Structured Bindings) این توانایی را برای ما می‌دهد تا متغیر‌های چند گانه از یک ساختار (struct) یا tuple را به هم دیگر متصل کنیم.

*مهمترین هدف Structured Bindings در نسخه‌ی ۱۷ ساده سازی و راحتی درک کد می‌باشد.

سینتکس این ویژگی به صورت زیر است:

auto ref-operator(optional)[identifier-list] = expression;

// Or

auto ref-operator(optional)[identifier-list]{expression};

// Or

auto ref-operator(optional)[identifier-list](expression);

اجازه دهید تا ما با استفاده ازیک مثال مزایای استفاده از ساختار‌های پیوندی را با کمک tuple ببینیم:

در نسخه‌ی ۹۸ سی‌پلاس‌پلاس:

#include <iostream>

using namespace std; 
  
// Creating a structure named Point 
struct Point { 
    int x; 
    int y; 
}; 
  
// Driver code 
int main() 
{ 
    Point p = {1, 2}; 
      
    int x_coord = p.x; 
    int y_coord = p.y; 
      
    cout << "X Coordinate : " << x_coord << endl; 
    cout << "Y Coordinate : " << y_coord << endl; 
  
    return 0; 
} 

در نسخه‌ی ۱۱ و ۱۴ سی‌پلاس‌پلاس:

#include <iostream>
#include <tuple> 
using namespace std; 
  
// Creating a structure named Point 
struct Point 
{ 
    int x, y; 
      
    // Default Constructor 
    Point() : x(0), y(0)  
    { 
          
    } 
      
    // Parameterized Constructor for Init List 
    Point(int x, int y) : x(x), y(y)  
    { 
          
    } 
    auto operator()() 
    { 
        // returns a tuple to make it work with std::tie 
        return make_tuple(x, y);  
    } 
}; 
  
// Driver code 
int main() 
{ 
    Point p = {1, 2}; 
    int x_coord, y_coord; 
    tie(x_coord, y_coord) = p(); 
      
    cout << "X Coordinate : " << x_coord << endl; 
    cout << "Y Coordinate : " << y_coord << endl; 
      
    return 0; 
} 
 

در نسخه‌‌ی ۱۷ سی‌پلاس‌پلاس:

#include <iostream>

using namespace std;

struct Point 
{ 
    int x; 
    int y; 
}; 
  
// Driver code 
int main( ) 
{ 
    Point p = { 1,2 }; 
      
    // Structure binding 
    auto[ x_coord, y_coord ] = p; 
      
    cout << "X Coordinate : " << x_coord << endl; 
    cout << "Y Coordinate : " << y_coord << endl; 
      
    return 0; 
} 

ویژگی عبارت شرطی و حلقه‌ی جدید

نسخه‌های جدید از دستورات شرطی switch و if در سی‌پلاس‌پلاس به صورت زیر هستند:

if (init; condition) و switch (init; condition)

قبلاً شما باید به صورت زیر یک دستور شرطی را پیاده سازی می‌کردید:

{   
    auto val = GetValue();   
    if (condition(val))    
        // on success  
    else   
        // on false... 
}

در این قالب مشخص است که val یک محدوده‌ی جداگانه و احتمال نشتی دارد. در نسخه‌ی جدید آن را می‌توان به صورت زیر ساده تر و خواناتر نوشت:

if (auto val = GetValue(); condition(val))    
    // on success  
else   
    // on false... 

در این نسخه val فقط در داخل حوزه‌ی if و else قابل مشاهده است، بنابراین در این صورت امکان نشتی نخواهد داشت. شرط ممکن است هر نوع شرط باشد و فقط وابسته به val مقدار true/false را بر نمی‌گرداند.

خُب، چرا این نسخه مفید خواهد بود؟

فرض کنید قرار است در داخل یک رشته چند چیز را جستجو کنید:

const std::string myString = "My Hello World Wow";

const auto it = myString.find("Hello");
if (it != std::string::npos)
    std::cout << it << " Hello\n"

const auto it2 = myString.find("World");
if (it2 != std::string::npos)
    std::cout << it2 << " World\n"

ما یا باید نام‌های مختلفی را برای it استفاده کنیم و یا باید آن‌ها را در داخل دامنه‌ی جداگانه قرار دهیم. مانند مثال زیر:

{
    const auto it = myString.find("Hello");
    if (it != std::string::npos)
        std::cout << it << " Hello\n"
}

{
    const auto it = myString.find("World");
    if (it != std::string::npos)
        std::cout << it << " World\n"
}

عبارت شرطی جدید if یک دامنه اضافی را فقط در یک خط ایجاد می‌کند:

if (const auto it = myString.find("Hello"); it != std::string::npos)
    std::cout << it << " Hello\n";

if (const auto it = myString.find("World"); it != std::string::npos)
    std::cout << it << " World\n";

همانطور که قبلاً ذکر شد متغیر تعریف شده در عبارت if نیز در بلوک else قابل مشاهده است. بنابراین شما می‌توانید آن را به صورت زیر نیز بنویسید:

if (const auto it = myString.find("World"); it != std::string::npos)
    std::cout << it << " World\n";
else
    std::cout << it << " not found!!\n";

همچنین شما در استاندارد جدید می‌توانید از ويژگی پیوند ساختاری در عبارت شرطی نیز استفاده کنید که قالب آن به صورت زیر است:

// better together: structured bindings + if initializer
if (auto [iter, succeeded] = mymap.insert(value); succeeded) {
    use(iter);  // ok
    // ...
} // iter and succeeded are destroyed here

ویژگی Variadic Templates

در نسخه‌ی ۱۱ ما ویژگی‌ خوبی به نام قالب‌های متنوع یا همان (Variadic Templates) داریم که بسیار عالی است، مخصوصاً وقتی که می‌خواهید با تعداد نامحدود یا متغیر با توابع کار کنید. برای مثال در نسخه‌های قبل از ۱۱ ما مجبور بودیم تا چندین تابع را با ورودی‌های مختلف پیاده سازی کنیم تا بتوانیم به نتیجه‌ی مربوطه برسیم.

در حال حاضر این ویژگی هنوز هم نیازمند افزودن کد‌های می‌باشد مخصوصاً اگر می‌خواهید تابعی از نوع بازگشتی پیاده سازی کنید. مانند مثال زیر:

auto SumCpp11(){
    return 0;
}

template<typename T1, typename... T>
auto SumCpp11(T1 s, T... ts){
    return s + SumCpp11(ts...);
}

در نسخه‌ی جدید سی++۱۷ ما می‌توانیم این را بسیار ساده تر بنویسیم:

template<typename ...Args> auto sum(Args ...args) 
{ 
    return (args + ... + 0); 
}

و یا حتی ساده تر...

template<typename ...Args> auto sum2(Args ...args) 
{ 
    return (args + ...);
}

این تابع فوق‌العاده است! ورودی‌های متغیر با نوع بازگشتی یکی از پر کاربرد‌ترین توابعی است که در نسخه‌های قبل پیاده سازی آن پیچیده بود.

ویژگی متغیر‌های درون خطی (Inline variables)

در قبل از سی++۱۷ ما می‌توانستیم از کلمه‌ی کلیدی inline جهت بهینه‌سازی در زمان کامپال برای توابع استفاده کنیم. حال در نسخه‌ی ۱۷ قابلیت تعریف inline برای متغیر‌ها نیز فراهم شده.

فرض کنید قرار است متغیری را تعریف کنیم که به صورت ایستا و عمومی مورد استفاده قرار بگیرد. در قبل از نسخه‌ی ۱۷ تعریف آن به این صورت که متغیر در فایل هدر و سورس اعلان و تعریف شوند:

#ifndef MYCLASS_H
#define MYCLASS_H


class MyClass
{
public:
    MyClass();

    static const int myVariable;
};

#endif // MYCLASS_H

فایل سورس

#include "myclass.h"

MyClass::MyClass()
{

}

const int MyClass::myVariable = 17;

و در نهایت تابع و فایل main:

#include <iostream>
#include "myclass.h"

int main()
{
    std::cout << "My global variable is : " << MyClass::myVariable << std::endl;
    return 0;

}

در استاندارد جدید تعریف تابع در همان زمان اعلان به صورت ایستا و عمومی امکان پذیر شده است. برای مثال:

#ifndef MYCLASS_H
#define MYCLASS_H


class MyClass
{
public:
    MyClass();

    inline static const int myVariable = 17;
};

#endif // MYCLASS_H

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

مثال‌های دیگر :

struct MyClass
{
    static const int sValue;
};

inline int const MyClass::sValue = 777;

و یا ساده تر از آن به شکل زیر:

struct MyClass
{
    inline static const int sValue = 777;
};

 

  • پسندیدن 6

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


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

برای ارسال دیدگاه یک حساب کاربری ایجاد کنید یا وارد حساب خود شوید

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

ایجاد یک حساب کاربری

برای حساب کاربری جدید در سایت ما ثبت نام کنید. عضویت خیلی ساده است !

ثبت نام یک حساب کاربری جدید

ورود به حساب کاربری

دارای حساب کاربری هستید؟ از اینجا وارد شوید

ورود به حساب کاربری

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

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

×