دفق C ++ كمعلمة عند مشغل التحميل الزائد <<
-
25-09-2019 - |
سؤال
أحاول كتابة فصل التسجيل الخاص بي واستخدامه كدفق:
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
).