ساختارهای C که هنوز در C++ کار نمیکنن
خلاصهٔ کاملتر
یه اصل قدیمی وجود داره که میگه C++ یه superset از C نیست — یعنی همهی کدهای C توی C++ کامپایل نمیشن. این اصل هنوز درسته، ولی با اومدن C++20 و C23 بعضی از کانکریتترین مثالهایی که آدمها میزدن عوض شدن. نویسنده در این مقاله یه بهروزرسانی جامع از مقالهی ۲۰۱۹ خودش ارائه میده و میگه دیگه نمیشه فقط از «کد C» یا «کد C++» حرف زد؛ باید نسخهی دقیق زبان رو هم مشخص کرد.
Designated Initializers یکی از مواردی بود که در C++17 وجود نداشت، ولی C++20 اونها رو اضافه کرد. با این حال نسخهی C++ محدودتره: فقط برای aggregate typeها کار میکنه، fieldها باید به ترتیب اعلامشون مقداردهی بشن، و نمیشه designated و positional رو با هم ترکیب کرد. مثلاً این کد در C معتبره ولی در C++ نه:
int table[4] = { [2] = 99 }; // valid C, invalid C++
struct Outer { struct Inner inner; };
struct Outer o = { .inner.value = 7 }; // valid C, invalid C++پارامترهای خالی تابع یکی دیگه از نقاط اختلاف کلاسیک بود. در C17 و قبلتر، نوشتن void fn() یه prototype محسوب نمیشد و فراخوانی اشتباه ممکن بود کامپایل بشه. C23 این رو اصلاح کرد و حالا void fn() مثل C++ رفتار میکنه — یعنی تابع هیچ پارامتری نمیگیره. برای headerهایی که هم در C و هم C++ استفاده میشن، هنوز void fn(void) امنترین انتخابه.
دربارهی malloc و چرخهی حیات آبجکت، مشکل اصلی اینه که C++ بر خلاف C، تبدیل ضمنی از void* رو نمیپذیره. ولی مشکل عمیقتر، lifetime آبجکتهاست. C++20 بعضی از کیسهای implicit-lifetime type رو اصلاح کرد، ولی این به معنی اجرای constructor یا مقداردهی اولیه نیست. استفاده از malloc برای typeهایی مثل std::string که constructor دارن، همچنان undefined behavior محسوب میشه. راهحل درست در C++ معمولاً std::make_unique یا placement new صریحه.
const_cast در C++ باید صریحاً نوشته بشه و یه cast از const برداشتن، به معنی مجاز بودن نوشتن روی اون آبجکت نیست. اگه آبجکت اصلی const باشه، نوشتن روی پوینتر cast شده undefined behaviorه.
Enumها هم تفاوت مهمی دارن. در C++، یه enum یه type مجزاست و نمیشه یه int رو بدون cast بهش نسبت داد. C23 هم مدل enum رو دقیقتر کرد ولی هنوز با مدل C++ فرق داره. برای کدهای C++، بهتره از enum class استفاده بشه که حتی تبدیل ضمنی به int هم نداره.
نکات کلیدی:
- C++ هنوز superset کامل C نیست؛ موقع صحبت از compatibility باید نسخهی دقیق زبان (C17، C23، C++20) رو ذکر کرد
- Designated initializers در C++20 اضافه شدن ولی محدودتر از نسخهی C هستن: ترتیب اعلام باید رعایت بشه و mixed clause مجاز نیست
- C23 قانون پارامترهای خالی تابع رو با C++ همراستا کرد، ولی برای headerهای مشترک هنوز void f(void) بهتره
- malloc در C++ نه فقط نیاز به cast داره، بلکه مسئلهی lifetime آبجکت هم مطرحه؛ برای typeهای با constructor از new یا make_unique استفاده کنید
- const_cast مجوز نوشتن روی آبجکت const نیست؛ فقط یه escape hatch محدوده
- در C++ برای enumها از enum class استفاده کنید تا از تبدیلهای ضمنی ناخواسته جلوگیری بشه




