لماذا يستدعي برنامج التحويل البرمجي المرئي C ++ الحمل الزائد الخاطئ هنا؟

StackOverflow https://stackoverflow.com/questions/811724

سؤال

لماذا يستدعي برنامج التحويل البرمجي المرئي C ++ الحمل الزائد الخاطئ هنا؟

لدي فئة فرعية من Ostream التي أستخدمها لتحديد المخزن المؤقت للتنسيق. في بعض الأحيان ، أرغب في إنشاء مؤقت وإدخال سلسلة على الفور مع المشغل المعتاد << مثل هذا:

M2Stream() << "the string";

لسوء الحظ ، يقوم البرنامج باستدعاء المشغل << (Ostream ، void *) الزائد العضو ، بدلاً من المشغل << (Ostream ، const char *) غير عضو.

كتبت العينة أدناه كاختبار حيث أقوم بتحديد فئتي M2stream التي تستنسخ المشكلة.

أعتقد أن المشكلة هي أن تعبير M2Stream () ينتج مؤقتًا وهذا يؤدي بطريقة ما إلى تفضيل المترجم إلى الحمل الزائد. لكن لماذا؟ يتم إثبات ذلك من خلال حقيقة أنه إذا قمت بوضع الحجة الأولى لحمل الزائد غير المفرط M2Stream & ، فأنا أحصل على غموض.

شيء غريب آخر هو أنه يدعو الحمل الزائد Const Char * المطلوب إذا قمت أولاً بتحديد متغير من النوع const char * ثم نسميه ، بدلاً من سلسلة Char الحرفية ، مثل هذا:

const char *s = "char string variable";
M2Stream() << s;  

يبدو الأمر كما لو أن السلسلة الحرفية لها نوع مختلف عن متغير Const Char *! ألا ينبغي أن يكونوا متماثلين؟ ولماذا يتسبب المترجم في مكالمة إلى الحمل الزائد * عندما أستخدم سلسلة Char المؤقتة والحرفية؟

#include "stdafx.h"
#include <iostream>
using namespace std;


class M2Stream
{
public:
    M2Stream &operator<<(void *vp)
    {
        cout << "M2Stream bad operator<<(void *) called with " << (const char *) vp << endl;
        return *this;
    }
};

/* If I make first arg const M2Stream &os, I get
\tests\t_stream_insertion_op\t_stream_insertion_op.cpp(39) : error C2666: 'M2Stream::operator <<' : 2 overloads have similar conversions
        \tests\t_stream_insertion_op\t_stream_insertion_op.cpp(13): could be 'M2Stream &M2Stream::operator <<(void *)'
        \tests\t_stream_insertion_op\t_stream_insertion_op.cpp(20): or 'const M2Stream &operator <<(const M2Stream &,const char *)'
        while trying to match the argument list '(M2Stream, const char [45])'
        note: qualification adjustment (const/volatile) may be causing the ambiguity
*/
const M2Stream & operator<<(M2Stream &os, const char *val)
{
    cout << "M2Stream good operator<<(const char *) called with " << val << endl;
    return os;
}


int main(int argc, char argv[])
{
    // This line calls void * overload, outputs: M2Stream bad operator<<(void *) called with literal char string on constructed temporary
    M2Stream() << "literal char string on constructed temporary";

    const char *s = "char string variable";

    // This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with char string variable
    M2Stream() << s;  

    // This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with literal char string on prebuilt object
    M2Stream m;
    m << "literal char string on prebuilt object";
    return 0;
}

انتاج:

M2Stream bad operator<<(void *) called with literal char string on constructed temporary
M2Stream good operator<<(const char *) called with char string variable
M2Stream good operator<<(const char *) called with literal char string on prebuilt object
هل كانت مفيدة؟

المحلول

يقوم المترجم بالشيء الصحيح: Stream() << "hello"; يجب استخدام operator<< تم تعريفها كدالة عضو. نظرًا لأن كائن الدفق المؤقت لا يمكن أن يكون مرتبطًا بمرجع غير مؤهل ولكن فقط إلى مرجع const ، فإن المشغل غير الأعضاء الذي يتعامل معه char const* لن يتم اختيارها.

وهو مصمم بهذه الطريقة ، كما ترى عند تغيير هذا المشغل. يمكنك الحصول على غموض ، لأن المترجم لا يمكن أن يقرر أي من المشغلين المتاحين الذين يجب استخدامه. لأنهم تم تصميمهم جميعًا برفض غير عضو operator<< في الاعتبار بالنسبة للذات.

ثم ، نعم ، الخيط الحرفي له نوع مختلف عن أ char const*. سلسلة حرفية هي مجموعة من أحرف const. لكن هذا لن يهم في قضيتك ، على ما أعتقد. لا أعرف ما هي الأحمال الزائدة operator<< MSVC ++ يضيف. يُسمح بإضافة المزيد من الأحمال الزائدة ، طالما أنها لا تؤثر على سلوك البرامج الصالحة.

لماذا M2Stream() << s; يعمل حتى عندما تكون المعلمة الأولى مرجعًا غير مؤلم ... حسنًا ، يحتوي MSVC ++ على امتداد يتيح المراجع غير المستقلة المرتبطة بالركض. ضع مستوى التحذير على المستوى 4 لرؤية تحذير من ذلك (شيء مثل "التمديد غير القياسي المستخدم ...").

الآن ، لأن هناك مشغل عضو << يأخذ ملف void const*, ، و char const* يمكن تحويل ذلك إلى ذلك ، سيتم اختيار هذا المشغل وسيتم إخراج العنوان لأن هذا ما void const* الحمل الزائد هو ل.

لقد رأيت في الكود الخاص بك أن لديك بالفعل void* الحمل الزائد ، وليس أ void const* الحمل الزائد. حسنًا ، يمكن للسلسلة أن تتحول إلى char*, ، على الرغم من أن نوع السلسلة الحرفية char const[N] (مع كون n هو مقدار الأحرف التي تضعها). لكن هذا التحويل قد تم إهماله. يجب أن لا يكون قياسيًا يتحول سلسلة حرفية إلى void*. يبدو لي أنه امتداد آخر من قبل برنامج التحويل البرمجي MSVC ++. لكن هذا من شأنه أن يفسر سبب معاملة الخيط الحرفي بشكل مختلف عن char const* مؤشر. هذا ما يقوله المعيار:

يمكن تحويل سلسلة حرفية (2.13.4) التي ليست عبارة عن سلسلة حرفية واسعة إلى rvalue من النوع "مؤشر إلى char" ؛ يمكن تحويل سلسلة واسعة حرفية إلى rvalue من النوع "مؤشر إلى wchar_t". في كلتا الحالتين ، والنتيجة هي مؤشر إلى العنصر الأول من الصفيف. يتم النظر في هذا التحويل فقط عندما يكون هناك نوع مستهدف مؤشر مناسب صريح ، وليس عندما تكون هناك حاجة عامة للتحويل من lvalue إلى rvalue. [ملاحظة: تم إهمال هذا التحويل. انظر الملحق D.

نصائح أخرى

المشكلة الأولى ناتجة عن قواعد لغة C ++ غريبة وصعبة:

  1. مؤقت تم إنشاؤه بواسطة مكالمة إلى مُنشئ rvalue.
  2. قد لا يكون rvalue مرتبطًا بمرجع غير مؤهل.
  3. ومع ذلك ، يمكن أن يكون للكائن RVALUE أساليب غير محتملة.

ما يحدث هو ذلك ostream& operator<<(ostream&, const char*), ، وظيفة غير عضو ، تحاول ربط M2Stream مؤقتًا تقوم بإنشاء مرجع غير مؤلم ، لكن هذا يفشل (القاعدة رقم 2) ؛ لكن ostream& ostream::operator<<(void*) هي وظيفة العضو وبالتالي يمكن أن ترتبط بها. في غياب const char* وظيفة ، يتم اختيارها كأفضل تحميل زائد.

لست متأكدًا لماذا قرر مصممي مكتبة iostreams القيام به operator<<() إلى عن على void* طريقة ولكن ليس operator<<() إلى عن على const char*, ، ولكن هذا ما هو عليه ، لذلك لدينا هذه التناقضات الغريبة للتعامل معها.

لست متأكدًا من سبب حدوث المشكلة الثانية. هل تحصل على نفس السلوك عبر مجمعين مختلفين؟ من المحتمل أن يكون بموجب برنامج التحويل البرمجي أو حشرة المكتبة القياسية C ++ ، لكنني أترك ذلك كذريعة للملجأ الأخير - على الأقل لمعرفة ما إذا كان يمكنك تكرار السلوك بمنتظم ostream أول.

المشكلة هي أنك تستخدم كائن دفق مؤقت. قم بتغيير الرمز إلى ما يلي وسيعمل:

M2Stream ms;
ms << "the string";

في الأساس ، يرفض التحويل البرمجي ربط المؤقتة إلى مرجع غير const.

فيما يتعلق بنقطة ثانية حول سبب ارتباطه عندما يكون لديك كائن "const char *" ، أعتقد أنه خطأ في برنامج التحويل البرمجي VC. ومع ذلك ، لا أستطيع أن أقول على وجه اليقين ، عندما يكون لديك سلسلة حرفية فقط ، هناك تحويل إلى "void *" وتحويل إلى "const char *". عندما يكون لديك كائن "const char *" ، فلا يوجد أي تحويل مطلوب في الوسيطة الثانية - وقد يكون هذا مشغلًا للسلوك غير القياسي لـ VC للسماح لربط غير Const Ref.

أعتقد أن 8.5.3/5 هو قسم المعيار الذي يغطي هذا.

لست متأكدًا من أن الكود الخاص بك يجب أن يجمع. أظن:

M2Stream & operator<<( void *vp )

يجب ان يكون:

M2Stream & operator<<( const void *vp )

في الواقع ، بالنظر إلى الكود أكثر ، أعتقد أن جميع مشاكلك تصل إلى كونست. الرمز التالي يعمل كما هو متوقع:

#include <iostream>
using namespace std;


class M2Stream
{
};

const M2Stream & operator<<( const M2Stream &os, const char *val)
{
    cout << "M2Stream good operator<<(const char *) called with " << val << endl;
    return os;
}


int main(int argc, char argv[])
{
    M2Stream() << "literal char string on constructed temporary";

    const char *s = "char string variable";

    // This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with char string variable
    M2Stream() << s;  

    // This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with literal char string on prebuilt object
    M2Stream m;
    m << "literal char string on prebuilt object";
    return 0;
}

يمكنك استخدام الحمل الزائد مثل هذا:

template <int N>
M2Stream & operator<<(M2Stream & m, char const (& param)[N])
{
     // output param
     return m;
}

كمكافأة إضافية ، أنت تعرف الآن أن طول الصفيف.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top