Manipulator iomanip manakah yang 'lengket'?

144

Saya baru-baru ini mengalami masalah saat membuat stringstreamkarena 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()dan std::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.
John K
sumber
Sebuah putaran kerja ada di sini: stackoverflow.com/a/37495361/984471
Manohar Reddy Poreddy

Jawaban:

91

Catatan penting dari komentar di bawah ini:

Oleh Martin:

@Chareles: Maka dengan persyaratan ini semua manipulator bersifat lengket. Kecuali setw yang sepertinya diatur ulang setelah digunakan.

Oleh Charles:

Persis! dan satu-satunya alasan setw tampaknya berperilaku berbeda adalah karena ada persyaratan pada operasi keluaran yang diformat untuk secara eksplisit .width (0) arus keluaran.

Berikut pembahasan yang mengarah pada kesimpulan di atas:


Melihat kode, manipulator berikut mengembalikan objek daripada aliran:

setiosflags
resetiosflags
setbase
setfill
setprecision
setw

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 setwlengket.

setiosflags:  Sticky
resetiosflags:Sticky
setbase:      Sticky
setfill:      Sticky
setprecision: Sticky

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 .

[no]boolalpha
[no]showbase
[no]showpoint
[no]showpos
[no]skipws
[no]unitbuf
[no]uppercase

dec/ hex/ oct

fixed/ scientific

internal/ left/ right

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
Martin York
sumber
Lembar contekan yang bagus. Tambahkan referensi ke mana informasi itu berasal, dan itu akan menjadi jawaban yang sempurna.
Mark Ransom
1
Namun saya dapat memverifikasi bahwa setfill () sebenarnya 'lengket' meskipun mengembalikan sebuah objek. Jadi saya pikir jawaban ini tidak benar.
John K
2
Objek yang mengembalikan aliran Harus lengket, sedangkan yang mengembalikan objek mungkin lengket tetapi tidak diperlukan. Saya akan memperbarui jawabannya dengan Info John.
Martin York
1
Saya tidak yakin apakah saya memahami alasan Anda. Semua manipulator yang mengambil parameter diimplementasikan sebagai fungsi bebas yang mengembalikan objek yang tidak ditentukan yang bekerja pada aliran ketika objek itu dimasukkan ke dalam aliran karena ini adalah satu-satunya cara (?) Untuk mempertahankan sintaks penyisipan dengan parameter. Either way, yang sesuai 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.
CB Bailey
3
Persis! dan satu-satunya alasan yang setwtampaknya berperilaku berbeda adalah karena ada persyaratan pada operasi keluaran yang diformat untuk .width(0)arus keluaran secara eksplisit .
CB Bailey
31

Alasan yang widthtampaknya 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_putkelebihan beban untuk num_puttemplate. Ini digunakan oleh kelebihan operator<<pengambilan basic_ostreamdan tipe numerik bawaan.

22.2.6.2.2 [lib.locale.money.put.virtuals]: Semua do_putkelebihan beban untuk money_puttemplate.

27.6.2.5.4 [lib.ostream.inserters.character]: Kelebihan operator<<pengambilan basic_ostreamdan salah satu tipe karakter dari instance basic_ostream atau char, ditandatangani charatau unsigned charatau pointer ke array tipe karakter ini.

Sejujurnya saya tidak yakin dengan alasan untuk ini, tetapi tidak ada status lain dari yang ostreamharus diatur ulang oleh fungsi output yang diformat. Tentu saja, hal-hal seperti badbitdan failbitdapat 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';
CB Bailey
sumber
6

setw()hanya mempengaruhi penyisipan berikutnya. Begitulah cara setw()berperilaku. Perilakunya setw()sama dengan ios_base::width(). Saya mendapatkan setw()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, rightdan internalmanipulator: Mereka seperti bendera lain dan jangan bertahan sampai berubah. Namun, mereka hanya berpengaruh jika lebar aliran diatur, dan lebar harus diatur setiap baris. Jadi misalnya

cout.width(6);
cout << right << "a" << endl;
cout.width(6);
cout << "b" << endl;
cout.width(6);
cout << "c" << endl;

akan memberimu

>     a
>     b
>     c

tapi

cout.width(6);
cout << right << "a" << endl;
cout << "b" << endl;
cout << "c" << endl;

akan memberimu

>     a
>b
>c

Manipulator Input dan Output tidak lengket dan hanya terjadi sekali saat digunakan. Manipulator parametrized masing-masing berbeda, berikut penjelasan singkatnya masing-masing:

setiosflagsmemungkinkan Anda menyetel bendera secara manual, yang daftarnya bisa menjadi sumber di sini , jadi ini melekat.

resetiosflagsberperilaku mirip dengan setiosflagskecuali 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").

setfillmengatur karakter isian untuk disisipkan di aliran saat setwdigunakan.

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 dalam setfill

David Brown
sumber
Nah, kebanyakan dari mereka hanya mengatur bendera, jadi itu "lengket." setw () tampaknya menjadi satu-satunya yang mempengaruhi hanya satu penyisipan. Anda dapat mengetahui lebih spesifik untuk masing-masing di cplusplus.com/reference/iostream/manipulators
David Brown
Yah std::hexjuga tidak lengket dan, jelas, std::flushatau std::setiosflagsjuga tidak lengket. Jadi menurut saya tidak sesederhana itu.
sbi
Hanya menguji hex dan setiosflags (), keduanya tampak lengket (keduanya hanya menyetel bendera yang bertahan untuk aliran itu sampai Anda mengubahnya).
David Brown
Ya, halaman web yang mengaku std::hextidak lengket ternyata salah - saya baru saja mengetahuinya juga. Bendera aliran, bagaimanapun, mungkin berubah bahkan jika Anda tidak menyisipkan std::setiosflagslagi, sehingga orang bisa melihatnya sebagai tidak lengket. Juga, std::wstidak lengket juga. Jadi ini bukan yang mudah.
sbi
Anda telah berusaha keras untuk meningkatkan jawaban Anda. +1
sbi