سؤال

أحاول كتابة فصل التسجيل الخاص بي واستخدامه كدفق:

logger L;
L << "whatever" << std::endl;

هذا هو الرمز الذي بدأت به:

#include <iostream>

using namespace std;


class logger{
public:
    template <typename T>
    friend logger& operator <<(logger& log, const T& value);
};

template <typename T>
logger& operator <<(logger& log, T const & value) {
    // Here I'd output the values to a file and stdout, etc.
    cout << value;
    return log;
}

int main(int argc, char *argv[])
{
    logger L;
    L << "hello" << '\n' ; // This works
    L << "bye" << "alo" << endl; // This doesn't work
    return 0;
}

لكنني كنت أحصل على خطأ عند محاولة التجميع ، قائلاً إنه لم يكن هناك تعريف للمشغل << (عند استخدام STD :: ENDL):

pruebaLog.cpp:31: error: no match for ‘operator<<’ in ‘operator<< [with T = char [4]](((logger&)((logger*)operator<< [with T = char [4]](((logger&)(& L)), ((const char (&)[4])"bye")))), ((const char (&)[4])"alo")) << std::endl’

لذلك ، كنت أحاول زيادة تحميل المشغل << لقبول هذا النوع من التدفقات ، لكنه يقودني إلى الجنون. أنا لا أعرف كيف نفعل ذلك. لقد تم إبطال ، على سبيل المثال ، تعريف STD :: endl في ملف رأس Ostream وكتبت وظيفة مع هذا الرأس:

logger& operator <<(logger& log, const basic_ostream<char,char_traits<char> >& (*s)(basic_ostream<char,char_traits<char> >&))

لكن لا حظ. لقد جربت نفس الشيء باستخدام القوالب بدلاً من استخدام Char مباشرة ، وحاولت أيضًا استخدام "Const Ostream & OS" ، ولا شيء.

شيء آخر يزعجني هو أنه في إخراج الخطأ ، فإن الوسيطة الأولى للمشغل << التغييرات ، وأحيانًا تكون إشارة إلى مؤشر ، وأحيانًا تبدو وكأنها مرجع مزدوج ...

هل كانت مفيدة؟

المحلول

endl هو وحش غريب. إنها ليست قيمة ثابتة. انها في الواقع ، من كل الأشياء ، وظيفة. تحتاج إلى تجاوز خاص للتعامل مع تطبيق endl:

logger& operator<< (logger& log, ostream& (*pf) (ostream&))
{
  cout << pf;
  return log;
}

هذا يقبل إدراج وظيفة تأخذ ostream المرجع ويعيد ostream المرجعي. وهذا ما endl هو.

تعديل: رداً على مسألة Franticpedantic المثيرة للاهتمام حول "لماذا لا يمكن للمترجم استنتاج هذا تلقائيًا؟". والسبب هو أنه إذا كنت تتخلى عن أعمق ، endl هو في الواقع نفسه أ نموذج وظيفة. يتم تعريفه على النحو التالي:

template <class charT, class traits>
  basic_ostream<charT,traits>& endl ( basic_ostream<charT,traits>& os );

وهذا هو ، يمكن أن يستغرق أي نوع من ostream كما المدخلات والمخرجات. لذا فإن المشكلة ليست أن المترجم لا يمكنه استنتاج ذلك T const & يمكن أن يكون مؤشر وظيفة ، ولكن لا يمكن معرفة ذلك أيّ endl كنت قصدت أن تمر. النسخة المحببة من operator<< المقدمة في السؤال من شأنه أن يقبل مؤشر لأي وظيفة كوسيطة ثانية لها ، ولكن في الوقت نفسه ، endl القالب يمثل لانهائي مجموعة من الوظائف المحتملة ، لذلك لا يمكن للمترجم أن يفعل أي شيء ذي معنى هناك.

توفير الحمل الزائد الخاص من operator<< الذي تطابق الحجة الثانية أ محدد إنشاء endl القالب يسمح للمكالمة لحل.

نصائح أخرى

endl هو معالج IO ، وهو المسلح الذي يقبل دفقًا بالرجوع إليه ، يقوم ببعض العمليات عليه ، ويعيد هذا الدفق ، أيضًا بالرجوع إليه. cout << endl يعادل cout << '\n' << flush, ، أين flush هو مناور الذي يطرد المخزن المؤقت الإخراج.

في فصلك ، تحتاج فقط إلى كتابة تحميل زائد لهذا المشغل:

logger& operator<<(logger&(*function)(logger&)) {
    return function(*this);
}

أين logger&(*)(logger&) هو نوع دالة قبول وإعادة أ logger بالتزكية. لكتابة معالجاتك الخاصة ، ما عليك سوى كتابة وظيفة تتطابق مع هذا التوقيع ، وجعلها تؤدي بعض العمليات على الدفق:

logger& newline(logger& L) {
    return L << '\n';
}

أعتقد أن المشكلة هي أن الدفق الخاص بك لا يحمل التحميل operator<< لقبول وظيفة لها نفس النوع std::endl كما هو موضح في هذه الإجابة: std :: endl من نوع غير معروف عند تشغيل المشغل الزائد <

في C ++ هو دفق المخزن المؤقت الذي يتضمن ميكانيمي I/O الأساسي. لا يلف الدفق نفسه سوى التحويلات إلى السلسلة ، واتجاه الإدخال/الإخراج.

وبالتالي ، يجب أن تستخدم إحدى فئات الدفق المحددة مسبقًا ، بدلاً من صنعها. إذا كان لديك هدف جديد ، فأنت تريد أن تذهب إلى I/O (مثل سجل النظام) ، ما يجب أن تنشئه هو خاص بك دفق المخزن المؤقت (مستمدة من std::streambuf).

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