Hangi iomanip manipülatörleri 'yapışkan'?


140

Son zamanlarda, açıkça değiştirinceye kadar, stringstreamyanlış bir şekilde std::setw()her ekleme için dize akışını etkileyeceğini varsaydığım için bir sorun yarattım. Ancak, yerleştirme işleminden sonra her zaman ayarlanmamıştır.

// 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'

Bir kaç sorum var:

  • Neden setw()bu şekilde?
  • Bu şekilde başka manipülatörler var mı?
  • Arasındaki davranış fark var mı std::ios_base::width()ve std::setw()?
  • Son olarak, bu davranışı açıkça belgeleyen çevrimiçi bir başvuru var mı? Satıcı belgelerim (MS Visual Studio 2005) bunu açıkça göstermiyor gibi görünüyor.

Yanıtlar:


87

Aşağıdaki yorumlardan önemli notlar:

Martin tarafından:

@Chareles: O zaman bu şartla tüm manipülatörler yapışkan. Kullanımdan sonra sıfırlanmış görünen setw hariç.

Charles tarafından:

Kesinlikle! ve setw'nin farklı davranmalarının tek nedeni, biçimlendirilmiş çıktı işlemlerinde çıktı akışını açıkça .width (0) yapmak için gereksinimler olmasıdır.

Yukarıdaki sonuca götüren tartışma aşağıdadır:


Koda bakarak aşağıdaki manipülatörler bir akış yerine bir nesne döndürür:

setiosflags
resetiosflags
setbase
setfill
setprecision
setw

Bu, yalnızca akışa uygulanan bir sonraki nesneye bir işlem uygulamak için yaygın bir tekniktir. Ne yazık ki bu yapışkan olmalarını engellemez. Testler, hepsi hariç setwhepsinin yapışkan olduğunu göstermektedir.

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

Diğer tüm manipülatörler bir akış nesnesi döndürür. Dolayısıyla, değiştirdikleri herhangi bir durum bilgisi akış nesnesine kaydedilmeli ve bu nedenle kalıcı olmalıdır (başka bir manipülatör durumu değiştirene kadar). Bu nedenle aşağıdaki manipülatörler Yapışkan manipülatörler olmalıdır .

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

dec/ hex/ oct

fixed/ scientific

internal/ left/ right

Bu manipülatörler aslında akış nesnesi yerine akışın kendisinde bir işlem gerçekleştirir (teknik olarak akış akış nesneleri durumunun bir parçası olmasına rağmen). Ancak akış nesnelerinin durumunun başka bir bölümünü etkilediklerine inanmıyorum.

ws/ endl/ ends/ flush

Sonuç olarak setw, versiyonumda yapışkan olmayan tek manipülatör gibi görünüyor.

Charles için zincirdeki sadece bir sonraki öğeyi etkilemek için basit bir hile:
İşte bir nesnenin durumu geçici olarak değiştirmek ve bir nesneyi kullanarak geri koymak için nasıl kullanılabileceğine dair bir Örnek:

#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

Güzel hile sayfası. Bilgilerin nereden geldiğine bir referans ekleyin ve mükemmel bir cevap olacaktır.
Mark Ransom

1
Ancak setfill () 'in bir nesne döndürmesine rağmen aslında' yapışkan 'olduğunu doğrulayabilirim. Bence bu cevap doğru değil.
John K

2
Bir akım geri nesneleri gerekir bir nesne dönmek bu yapışkan olabilir, yapışkan olabilir, ancak bu gerekli değildir. Cevabı John's Info ile güncelleyeceğim.
Martin York

1
Akıl yürütmenizi anladığımdan emin değilim. Parametreleri alan tüm manipülatörler, parametrelerle ekleme sözdizimini korumanın tek (?) Yolu olduğundan, bu nesne akışa eklendiğinde bir akış üzerinde hareket eden belirtilmemiş bir nesneyi döndüren serbest işlevler olarak uygulanır. Her iki durumda da operator<<, manipülatör için uygun olan akış durumunun belirli bir şekilde değiştirilmesini sağlar. Her iki form da herhangi bir devlet nöbetçisi kurmaz. Durumun hangi bölümünün sıfırlandığını belirleyen yalnızca bir sonraki biçimlendirilmiş ekleme işleminin davranışıdır.
CB Bailey

3
Kesinlikle! ve setwfarklı davrandığı görülen tek neden , biçimlendirilmiş çıktı işlemlerinde açıkça .width(0)çıktı akışına yönelik gereksinimler olmasıdır .
CB Bailey

31

width'Yapışkan' görünmemesinin nedeni, belirli işlemlerin .width(0)bir çıktı akışında çağrılabileceğinin garantisidir . Onlar:

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]: Şablon do_putiçin tüm aşırı yüklemeler num_put. Bunlar, operator<<a basic_ostreamve yerleşik sayısal tiplerin aşırı yüklenmesi ile kullanılır .

22.2.6.2.2 [lib.locale.money.put.virtuals]: Şablon do_putiçin tüm aşırı yüklemeler money_put.

27.6.2.5.4 [lib.ostream.inserters.character]: içinde Overloads operator<<bir alma basic_ostreamve basic_ostream örnekleme char türü bir ya da char, imza charya daunsigned char veya işaretçiler bu karakter türleri dizileri için.

Dürüst olmak gerekirse, bunun gerekçesinden emin değilim, ancak başka hiçbir durumun ostreambiçimlendirilmiş çıktı işlevleri tarafından sıfırlanmaması gerekir. Tabii ki, badbitvefailbit çıkış işleminde bir hata varsa böyle olması beklenmelidir.

Genişliği sıfırlamak için düşünebilmemin tek nedeni, bazı sınırlandırılmış alanların çıktısını almaya çalışırken sınırlayıcılarınızın dolgulu olması şaşırtıcı olabilir.

Örneğin

std::cout << std::setw(6) << 4.5 << '|' << 3.6 << '\n';

"   4.5     |   3.6      \n"

'Düzeltmek' için:

std::cout << std::setw(6) << 4.5 << std::setw(0) << '|' << std::setw(6) << 3.6 << std::setw(0) << '\n';

sıfırlama genişliğiyle, istenen çıktı daha kısa ile üretilebilir:

std::cout << std::setw(6) << 4.5 << '|' << std::setw(6) << 3.6 << '\n';

6

setw()yalnızca sonraki eklemeyi etkiler. Bu sadece bir yol setw()davranacağını. Davranışı ile setw()aynıdır ios_base::width(). Benim var setw()bilgi cplusplus.com .

Burada manipülatörlerin tam bir listesini bulabilirsiniz . Bu bağlantıdan, tüm akış bayrakları başka bir manipülatör tarafından değiştirilene kadar set demelidir. Bir hakkında not left, rightve internalmanipülatörler: Onlar diğer bayraklar gibi ve do değiştirilene kadar sürer. Bununla birlikte, yalnızca akışın genişliği ayarlandığında bir etkisi vardır ve genişlik her satırda ayarlanmalıdır. Yani mesela

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

sana veririm

>     a
>     b
>     c

fakat

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

sana veririm

>     a
>b
>c

Giriş ve Çıkış manipülatörleri yapışkan değildir ve yalnızca kullanıldıkları zaman meydana gelirler. Parametreli manipülatörlerin her biri farklıdır, işte bunların her birinin kısa bir açıklaması:

setiosflagsbir listesini buraya yerleştirilebilecek bayrakları manuel olarak ayarlamanıza olanak tanır , bu nedenle yapışkandır.

resetiosflagssetiosflagsbelirtilen bayrakları kaldırması dışındakine benzer şekilde davranır .

setbase akışa eklenen tamsayıların tabanını ayarlar (böylece taban 16'daki 17 "11" ve taban 2'deki "10001" olacaktır).

setfillsetwkullanıldığında akışa eklenecek dolgu karakterini ayarlar .

setprecision kayan nokta değerleri eklenirken kullanılacak ondalık duyarlığı ayarlar.

setw içinde belirtilen karakteri doldurarak yalnızca sonraki ekleme belirtilen genişliği yapar setfill


Çoğu, sadece bayraklar ayarlıyorlar, bu yüzden bunlar "yapışkan". setw () yalnızca bir eklemeyi etkileyen tek şey gibi görünüyor. Her biri için daha fazla bilgiyi cplusplus.com/reference/iostream/manipulators
David Brown

İyi std::hexda yapışkan değildir ve, tabii ki, std::flushya da std::setiosflagsya da yapışkan değildir. Bu kadar basit olduğunu sanmıyorum.
sbi

Sadece hex ve setiosflags () 'ı test ediyorlar, her ikisi de yapışkan görünüyor (ikisi de siz değiştirene kadar o akış için kalıcı olan bayrakları ayarlıyorlar).
David Brown

Evet, std::hexyapışkan olmadığını iddia eden web sayfası yanlıştı - bunu ben de öğrendim. Bununla birlikte, akış bayrakları bir std::setiosflagsdaha eklemeseniz bile değişebilir , bu nedenle yapışkan olmayan olarak görülebilir. Ayrıca, std::wsyapışkan değil. Bu yüzden olduğunu değil o kolay.
sbi

Cevabınızı geliştirmek için epey çaba harcadınız. +1
sbi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.