c++ - هل يمكن أن يؤثر الإعلان على مساحة الاسم std؟




reserved (2)

الكود الخاص بك يسبب سلوك غير محدد.

C ++ 17 [extern.names] / 4:

يتم حجز كل توقيع دالة من مكتبة C القياسية التي تم الإعلان عنها باستخدام ارتباط خارجي للتنفيذ لاستخدامه كتوقيع وظيفة مع كل من الارتباط "C" الخارجي و "C ++" الخارجي أو كاسم نطاق مساحة الاسم في مساحة الاسم العالمية.

لذلك لا يمكنك إنشاء دالة بنفس النموذج الأولي مثل وظيفة مكتبة C القياسية int abs(int); . بغض النظر عن الرؤوس التي تقوم بتضمينها بالفعل أو ما إذا كانت تلك الرؤوس تضع أيضًا أسماء مكتبات C في مساحة الاسم العالمية.

ومع ذلك ، سوف يُسمح بتجاوز abs إذا قمت بتوفير أنواع معلمات مختلفة.

#include <iostream>
#include <cmath>

/* Intentionally incorrect abs() which seems to override std::abs() */
int abs(int a) {
    return a > 0? -a : a;
}

int main() {
    int a = abs(-5);
    int b = std::abs(-5);
    std::cout<< a << std::endl << b << std::endl;
    return 0;
}

كنت أتوقع أن يكون الناتج -5 و 5 ، ولكن الإخراج هو -5 و -5 .

أتساءل لماذا ستحدث هذه الحالة؟

هل لديها أي علاقة مع استخدام std أم ماذا؟


allows مواصفات اللغة <cmath> بتنفيذ <cmath> خلال الإعلان عن (وتحديد) الوظائف القياسية في مساحة الاسم العالمية ثم نقلها إلى مساحة الاسم std عن طريق استخدام التعريفات. إنه غير محدد ما إذا كان يتم استخدام هذا النهج

20.5.1.2 الرؤوس
4 [...] في مكتبة C ++ القياسية ، ومع ذلك ، فإن التعريفات (باستثناء الأسماء التي تم تعريفها على أنها وحدات الماكرو في C) تقع ضمن نطاق مساحة الاسم (6.3.6) من مساحة الاسم std . لم يتم تحديد ما إذا كانت هذه الأسماء (بما في ذلك أي الأحمال الزائدة المضافة في الفقرات 21 إلى 33 والملحق D) قد تم الإعلان عنها أولاً ضمن نطاق مساحة الاسم العالمية ثم يتم حقنها في مساحة الاسم std عن طريق استخدام إعلانات صريحة (10.3.3).

يبدو أنك تتعامل مع أحد التطبيقات التي قررت اتباع هذا النهج (مثل مجلس التعاون الخليجي). بمعنى أن تطبيقك يوفر ::abs ، في حين يشير std::abs ببساطة إلى " ::abs .

سؤال واحد يبقى في هذه الحالة هو لماذا ، بالإضافة إلى المعيار ::abs ، تمكنت من إعلان ملكيتك ::abs ، أي لماذا لا يوجد خطأ في التعريف المتعدد. قد يكون السبب في ذلك ميزة أخرى توفرها بعض التطبيقات (مثل GCC): فهي تعلن عن الوظائف القياسية باسم الرموز الضعيفة ، مما يتيح لك "استبدالها" بتعريفاتك الخاصة.

ينشئ هذان العاملان معًا التأثير الذي تلاحظه: استبدال رمز ضعيف لـ ::abs يؤدي أيضًا إلى استبدال std::abs . مدى توافق هذا مع معيار اللغة هو قصة مختلفة ... على أي حال ، لا تعتمد على هذا السلوك - لا تضمنه اللغة.

في دول مجلس التعاون الخليجي ، يمكن استنساخ هذا السلوك من خلال المثال البسيط التالي. ملف مصدر واحد

#include <iostream>

void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }

ملف مصدر آخر

#include <iostream>

void foo();
namespace N { using ::foo; }

void foo() { std::cout << "Goodbye!" << std::endl; }

int main()
{
  foo();
  N::foo();
}

في هذه الحالة ، ستلاحظ أيضًا أن التعريف الجديد لـ ::foo ( "Goodbye!" ) في الملف المصدر الثاني يؤثر أيضًا على سلوك N::foo . سوف كلا المكالمات إخراج "Goodbye!" . وإذا قمت بإزالة تعريف ::foo من الملف المصدر الثاني ، فسيتم إرسال كلا المكالمات إلى التعريف "الأصلي" لـ ::foo وإخراج "Hello!" .

إذن الذي قدمه أعلاه 20.5.1.2/4 هناك لتبسيط تنفيذ <cmath> . يُسمح للتطبيقات ببساطة بتضمين النمط C <math.h> ، ثم إعادة تحديد الوظائف في std وإضافة بعض الإضافات والقرص C ++ المحددة. إذا كان الشرح أعلاه يصف بشكل صحيح الآليات الداخلية للمشكلة ، فإن جزءًا كبيرًا منها يعتمد على إمكانية استبدال الرموز الضعيفة للإصدارات C من الوظائف.

لاحظ أنه إذا استبدلنا عالميًا int بـ double في البرنامج أعلاه ، فسوف يتصرف الرمز (بموجب GCC) "كما هو متوقع" - فسيتم إخراجه -5 5 . يحدث هذا لأن المكتبة القياسية C لا تحتوي على وظيفة abs(double) . بإعلان abs(double) الخاصة بنا abs(double) ، فإننا لا نستبدل أي شيء.

ولكن إذا انتقلنا من abs int إلى double فإننا ننتقل أيضًا من abs إلى fabs ، وسيظهر السلوك الأصلي الأصلي في مجده الكامل (الناتج -5 -5 ).

هذا يتفق مع الشرح أعلاه.





reserved