Saya baru-baru ini mengalami masalah saat membuat stringstream
karena fakta bahwa saya salah mengasumsikan std::setw()
akan memengaruhi aliran string untuk setiap penyisipan, sampai saya mengubahnya secara eksplisit. Namun, itu selalu tidak disetel setelah penyisipan.
// With timestruct with value of 'Oct 7 9:04 AM'
std::stringstream ss;
ss.fill('0'); ss.setf(ios::right, ios::adjustfield);
ss << setw(2) << timestruct.tm_mday;
ss << timestruct.tm_hour;
ss << timestruct.tm_min;
std::string filingTime = ss.str(); // BAD: '0794'
Jadi, saya punya sejumlah pertanyaan:
- Kenapa
setw()
begini? - Apakah ada manipulator lain seperti ini?
- Apakah ada perbedaan perilaku antara
std::ios_base::width()
danstd::setw()
? - Terakhir, apakah ada referensi online yang dengan jelas mendokumentasikan perilaku ini? Dokumentasi vendor saya (MS Visual Studio 2005) tampaknya tidak menunjukkan ini dengan jelas.
Jawaban:
Catatan penting dari komentar di bawah ini:
Oleh Martin:
Oleh Charles:
Berikut pembahasan yang mengarah pada kesimpulan di atas:
Melihat kode, manipulator berikut mengembalikan objek daripada aliran:
Ini adalah teknik umum untuk menerapkan operasi hanya ke objek berikutnya yang diterapkan ke aliran. Sayangnya ini tidak menghalangi mereka untuk menjadi lengket. Pengujian menunjukkan bahwa semuanya kecuali
setw
lengket.Semua manipulator lainnya mengembalikan objek aliran. Jadi, informasi status apa pun yang mereka ubah harus direkam dalam objek aliran dan dengan demikian permanen (hingga manipulator lain mengubah status). Jadi, manipulator berikut haruslah manipulator Sticky .
Manipulator ini sebenarnya melakukan operasi pada aliran itu sendiri daripada objek aliran (Meskipun secara teknis aliran adalah bagian dari status objek aliran). Tapi saya tidak percaya mereka mempengaruhi bagian lain dari keadaan objek arus.
ws/ endl/ ends/ flush
Kesimpulannya adalah setw sepertinya satu-satunya manipulator di versi saya yang tidak lengket.
Untuk Charles trik sederhana untuk mempengaruhi hanya item berikutnya dalam rantai:
Berikut adalah Contoh bagaimana sebuah objek dapat digunakan untuk mengubah keadaan sementara kemudian mengembalikannya dengan menggunakan objek:
#include <iostream> #include <iomanip> // Private object constructed by the format object PutSquareBracket struct SquareBracktAroundNextItem { SquareBracktAroundNextItem(std::ostream& str) :m_str(str) {} std::ostream& m_str; }; // New Format Object struct PutSquareBracket {}; // Format object passed to stream. // All it does is return an object that can maintain state away from the // stream object (so that it is not STICKY) SquareBracktAroundNextItem operator<<(std::ostream& str,PutSquareBracket const& data) { return SquareBracktAroundNextItem(str); } // The Non Sticky formatting. // Here we temporariy set formating to fixed with a precision of 10. // After the next value is printed we return the stream to the original state // Then return the stream for normal processing. template<typename T> std::ostream& operator<<(SquareBracktAroundNextItem const& bracket,T const& data) { std::ios_base::fmtflags flags = bracket.m_str.flags(); std::streamsize currentPrecision = bracket.m_str.precision(); bracket.m_str << '[' << std::fixed << std::setprecision(10) << data << std::setprecision(currentPrecision) << ']'; bracket.m_str.flags(flags); return bracket.m_str; } int main() { std::cout << 5.34 << "\n" // Before << PutSquareBracket() << 5.34 << "\n" // Temp change settings. << 5.34 << "\n"; // After } > ./a.out 5.34 [5.3400000000] 5.34
sumber
operator<<
untuk manipulator memastikan bahwa status aliran diubah dengan cara tertentu. Tak satu pun dari formulir itu yang membentuk penjaga negara jenis apa pun. Hanya perilaku operasi penyisipan berformat berikutnya yang menentukan bagian mana dari keadaan yang disetel ulang jika ada.setw
tampaknya berperilaku berbeda adalah karena ada persyaratan pada operasi keluaran yang diformat untuk.width(0)
arus keluaran secara eksplisit .Alasan yang
width
tampaknya tidak 'melekat' adalah karena operasi tertentu dijamin akan memanggil.width(0)
aliran keluaran. Yaitu:21.3.7.9 [lib.string.io]:
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT,traits,Allocator>& str);
22.2.2.2.2 [lib.facet.num.put.virtuals]: Semua
do_put
kelebihan beban untuknum_put
template. Ini digunakan oleh kelebihanoperator<<
pengambilanbasic_ostream
dan tipe numerik bawaan.22.2.6.2.2 [lib.locale.money.put.virtuals]: Semua
do_put
kelebihan beban untukmoney_put
template.27.6.2.5.4 [lib.ostream.inserters.character]: Kelebihan
operator<<
pengambilanbasic_ostream
dan salah satu tipe karakter dari instance basic_ostream atauchar
, ditandatanganichar
atauunsigned char
atau pointer ke array tipe karakter ini.Sejujurnya saya tidak yakin dengan alasan untuk ini, tetapi tidak ada status lain dari yang
ostream
harus diatur ulang oleh fungsi output yang diformat. Tentu saja, hal-hal sepertibadbit
danfailbit
dapat diatur jika ada kegagalan dalam operasi keluaran, tetapi itu harus diharapkan.Satu-satunya alasan yang dapat saya pikirkan untuk menyetel ulang lebar adalah karena mungkin mengejutkan jika, saat mencoba menampilkan beberapa bidang yang dipisahkan, pembatas Anda diberi bantalan.
Misalnya
std::cout << std::setw(6) << 4.5 << '|' << 3.6 << '\n'; " 4.5 | 3.6 \n"
Untuk 'mengoreksi' ini akan membutuhkan:
std::cout << std::setw(6) << 4.5 << std::setw(0) << '|' << std::setw(6) << 3.6 << std::setw(0) << '\n';
sedangkan dengan lebar pengaturan ulang, keluaran yang diinginkan dapat dihasilkan dengan lebih pendek:
std::cout << std::setw(6) << 4.5 << '|' << std::setw(6) << 3.6 << '\n';
sumber
setw()
hanya mempengaruhi penyisipan berikutnya. Begitulah carasetw()
berperilaku. Perilakunyasetw()
sama denganios_base::width()
. Saya mendapatkansetw()
informasi saya dari cplusplus.com .Anda dapat menemukan daftar lengkap manipulator di sini . Dari tautan itu, semua tanda aliran harus mengatakan setel hingga diubah oleh manipulator lain. Satu catatan tentang
left
,right
daninternal
manipulator: Mereka seperti bendera lain dan jangan bertahan sampai berubah. Namun, mereka hanya berpengaruh jika lebar aliran diatur, dan lebar harus diatur setiap baris. Jadi misalnyacout.width(6); cout << right << "a" << endl; cout.width(6); cout << "b" << endl; cout.width(6); cout << "c" << endl;
akan memberimu
tapi
cout.width(6); cout << right << "a" << endl; cout << "b" << endl; cout << "c" << endl;
akan memberimu
Manipulator Input dan Output tidak lengket dan hanya terjadi sekali saat digunakan. Manipulator parametrized masing-masing berbeda, berikut penjelasan singkatnya masing-masing:
setiosflags
memungkinkan Anda menyetel bendera secara manual, yang daftarnya bisa menjadi sumber di sini , jadi ini melekat.resetiosflags
berperilaku mirip dengansetiosflags
kecuali itu tidak menyetel bendera yang ditentukan.setbase
menetapkan basis bilangan bulat yang dimasukkan ke dalam aliran (jadi 17 di basis 16 akan menjadi "11", dan di basis 2 akan menjadi "10001").setfill
mengatur karakter isian untuk disisipkan di aliran saatsetw
digunakan.setprecision
mengatur ketepatan desimal yang akan digunakan saat memasukkan nilai titik mengambang.setw
membuat hanya penyisipan berikutnya dengan lebar yang ditentukan dengan mengisi karakter yang ditentukan dalamsetfill
sumber
std::hex
juga tidak lengket dan, jelas,std::flush
ataustd::setiosflags
juga tidak lengket. Jadi menurut saya tidak sesederhana itu.std::hex
tidak lengket ternyata salah - saya baru saja mengetahuinya juga. Bendera aliran, bagaimanapun, mungkin berubah bahkan jika Anda tidak menyisipkanstd::setiosflags
lagi, sehingga orang bisa melihatnya sebagai tidak lengket. Juga,std::ws
tidak lengket juga. Jadi ini bukan yang mudah.