در این پست قصد داریم یک عملگر جدید بجای typedef معرفی کنیم که از C++11 به استاندارد های زبان سی پلاس پلاس اضافه شده

خوب اول کاربرد typedef را یادآوری میکنیم:

عملگر typedef برای تعریف کردن یک تایپ(Data Type) جدید استفاده میشه. مثلا زمانی که یک تایپ طول زیادی داره میتونیم یک تایپ جدید تعریف کنیم که طول کمتری داشته باشه، برای مثال:

typedef   std::unique_ptr<std_unordered_map<std::string, std::string>>   UPtrMapSS;

عملگر typedef از C++98 به یادگار مانده که البته در C++11 هم کار میکنه، اما استاندارد های C++11 پیشنهاد استفاده از alias declaration را داده:

using   UPtrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>;

 عملگر typedef و alias declaration کار مشابهی انجام میدهند، اما به دلایلی استفاده از alias declaration به typedef ترجیح داده میشه که به آن ها خواهیم پرداخت.

البته قبل از اینکه به این دلایل بپردازیم، این نکته را ذکر کنم که استفاده از alias declaration برای کار با اشاره گر به تابع بسیار ساده تر از typedef خواهد بود، به مثال زیر توجه کنید:

typedef   void (* FP)(int, const std::string&);

using   FP = void (*) (int, const std::string&);

در مثال بالا FP یک اشاره گر به تابعی است که پارامترهای ورودی آن یک عدد صحیح و یک رشته است و خروجی آن تهی(void) است.

خوب حالا بپردازیم به دلایل استفاده از alias declaration بجای typedef:

یک دلیل متقاعد کننده برای استفاده از alias declaration بجای typedef مربوط میشه به template ها در سی پلاس پلاس

alias declaration ها میتوانند به صورت template تعریف بشوند در حالی که این امکان برای typedef به سختی و همراه با پیچیدگی همراه است، مثال:

فرض کنید میخواهیم یک تایپ جدید برای std::list تعریف کنیم که از یک کلاس allocator مخصوص به نام MyAllocator استفاده میکند:

template   <typename T>

using   MyAllocList = std::list<T, MyAllocator<T>>;

حال اگر بخواهیم از این تایپ جدید توی برنامه استفاده کنیم:

MyAllocList<Widget>   lw;

typedef را نمیتوان به صورت template تعریف کرد، اما برنامه نویسان C++98 از ترفند زیر استفاده میکنند(به این روش nested typedef میگویند) :

template   <typename T>

struct   MyAllocList{

   typedef   std::list<T, MyAllocator<T>>   type;

};

حال اگر بخواهیم از این تایپ توی برنامه استفاده کنیم:

MyAllocList<widget>::type   lw;

اما اگر بخواهیم از این typedef داخل یک کلاس که به صورت template پیاده سازی شده است استفاده کنیم:

template<typename T>

class   widget{

private:

   MyAllocList<T>::type   list;

};

کد بالا کامپایل نخواهد شد ):

برای کامپایل کردن قطعه کد بالا باید قبل از MyAllocList از typename استفاده کنیم:

template<typename T>

class   widget{

private:

   typename   MyAllocList<T>::type   list;

};

MyAllocList<T>::type به یک تایپی اشاره میکند که به پارامتر T (که مربوط به template است) وابسته است. بنابراین به MyAllocList<T>::type یک تایپ وابسته یا dependent type میگویند. و به طور کلی در قوانین زبان سی پلاس پلاس قبل از اسم یک تایپ وابسته باید کلمه typename بیاید

اما اگر MyAllocList به صورت alias declaration تعریف شود دیگر نیازی به typename نبود:

template<typename T>

using   MyAllocList = std::list<T, MyAllocator<T>;

template<typename T>

class   widget{

private:

   MyAllocList<T>  list;

};

شاید تو نگاه اول به نظرتون عجیب بیاد، چون ممکنه بگید که در حالت alias declaration هم MyAllocList یک تایپ وابسته است و فرقی با حالت typedef نداره!!!!!

اما کامپایلر مثل شما فکر نمیکنه :)

بزارید یک مقدار بیشتر در مورد عملکرد کامپایلر توضیح بدم، کد زیر را در نظر بگیرید:

template<typename T>

using   MyAllocList = std::list<T, MyAllocator<T>>;

template <typename T>

class    widget{

private:

   MyAllocList<T>   type;   

};

در کد بالا وقتی کامپایلر با MyAllocList<T> type مواجه میشه، اطمینان داره که MyAllocList یک تایپ منحصر به فرد هستش و چیز دیگه ای نمیتونه باشه، چون  در کد بالا نمیشه دو تا MyAllocList وجود داشته باشه و MyAllocList کاملا منحصر به فرد هستش.

حالا قطعه کد زیر را در نظر بگیرید:

template   <typename T>

struct   MyAllocList{

   typedef   std::list<T, MyAllocator>   type;

}

template   <typename T>

class   widget{

private:

   MyAllocList<T>::type   list;

};

در قطعه کد بالا وقتی کامپایلر با MyAllocList<T>::type مواجه میشه، به هیچ وجه نمیتونه اطمینان خاطر پیدا کنه که MyAllocList<T>::type حتما یک تایپ هستش چون ممکنه جلوتر یک special template از MyAllocList باشه که در آن type یک عضو از کلاس باشه(member) و تایپ نباشه:

class wine{

};

template<>

class   MyAllocList<wine>{

   enum class typeWine{Red , Blue, Rose};

   typeWine   type;

}

به همین خاطر کامپایلر برای اطمینان خاطر شما را مجبور میکنه تا typename را قبل از MyAllocList<T>::type بنویسید:

template   <typename T>

struct   MyAllocList{

   typedef   std::list<T, MyAllocator>   type;

}

template   <typename T>

class   widget{

private:

   typename   MyAllocList<T>::type   list;

};