Niedawno miałem problem z utworzeniem pliku stringstream
, ponieważ błędnie założyłem, std::setw()
że wpłynie to na strumień ciągu dla każdego wstawienia, dopóki nie zmienię go wyraźnie. Jednak po włożeniu jest zawsze rozbrojony.
// 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'
Mam więc kilka pytań:
- Dlaczego tak jest
setw()
? - Czy są tacy manipulatorzy?
- Czy istnieje różnica w zachowaniu między
std::ios_base::width()
istd::setw()
? - Wreszcie, czy istnieje odniesienie online, które jasno dokumentuje to zachowanie? Dokumentacja mojego dostawcy (MS Visual Studio 2005) nie wydaje się tego jasno pokazywać.
Odpowiedzi:
Ważne uwagi z poniższych komentarzy:
Martin:
Charles:
Poniżej znajduje się dyskusja prowadząca do powyższego wniosku:
Patrząc na kod, następujące manipulatory zwracają obiekt, a nie strumień:
Jest to powszechna technika stosowania operacji tylko do następnego obiektu, który jest stosowany do strumienia. Niestety nie wyklucza to ich lepkości. Testy wskazują, że wszystkie z wyjątkiem
setw
są lepkie.Wszystkie inne manipulatory zwracają obiekt strumienia. W związku z tym wszelkie informacje o stanie, które zmieniają, muszą być zapisane w obiekcie strumienia i dlatego są trwałe (do czasu zmiany stanu przez innego manipulatora). Zatem następujące manipulatory muszą być manipulatorami Sticky .
Te manipulatory w rzeczywistości wykonują operację na samym strumieniu, a nie na obiekcie strumienia (chociaż technicznie strumień jest częścią stanu obiektów strumienia). Ale nie wierzę, że wpływają one na jakąkolwiek inną część stanu obiektów strumienia.
Wniosek jest taki, że setw wydaje się być jedynym manipulatorem w mojej wersji, który nie jest lepki.
Dla Charlesa prosta sztuczka polegająca na wpłynięciu tylko na następny element w łańcuchu:
Oto przykład, w jaki sposób obiekt może być użyty do czasowej zmiany stanu, a następnie przywrócenia go za pomocą obiektu:
źródło
operator<<
dla manipulatora zapewnia, że stan strumienia zmienia się w określony sposób. Żaden z formularzy nie ustanawia żadnego rodzaju strażnika państwowego. Tylko zachowanie następnej sformatowanej operacji wstawiania określa, która część stanu jest resetowana, jeśli w ogóle.setw
wydaje się, że zachowuje się inaczej, jest to, że istnieją wymagania dotyczące sformatowanych operacji wyjściowych, które jawnie dotyczą.width(0)
strumienia wyjściowego.Przyczyną,
width
która nie wydaje się być „lepka”, jest to, że pewne operacje wywołują.width(0)
strumień wyjściowy. To są:21.3.7.9 [lib.string.io]:
22.2.2.2.2 [lib.facet.num.put.virtuals]: Wszystkie
do_put
przeciążenianum_put
szablonu. Są one używane przez przeciążeniaoperator<<
przyjmowania abasic_ostream
i wbudowanego typu liczbowego.22.2.6.2.2 [lib.locale.money.put.virtuals]: Wszystkie
do_put
przeciążeniamoney_put
szablonu.27.6.2.5.4 [lib.ostream.inserters.character]: Przeciążenie związane z
operator<<
pobieraniem abasic_ostream
i jednego typu char instancji basic_ostream lubchar
, signedchar
lubunsigned char
lub wskaźniki do tablic tych typów char.Szczerze mówiąc, nie jestem pewien, dlaczego tak jest, ale żadne inne stany nie
ostream
powinny być resetowane przez sformatowane funkcje wyjściowe. Oczywiście takie rzeczy jakbadbit
ifailbit
mogą być ustawione, jeśli wystąpi błąd w działaniu wyjścia, ale należy się tego spodziewać.Jedynym powodem, dla którego przychodzi mi do głowy, aby zresetować szerokość, jest to, że może być zaskakujące, jeśli podczas próby wyprowadzenia niektórych pól rozdzielanych ograniczniki zostały wypełnione.
Na przykład
Aby to „poprawić”, należałoby:
podczas gdy przy zerowaniu szerokości, pożądane wyjście można wygenerować z krótszymi:
źródło
setw()
wpływa tylko na następne wstawienie. Po prostu tak sięsetw()
zachowuje. Zachowaniesetw()
jest takie samo jakios_base::width()
. Mamsetw()
informacje z cplusplus.com .Pełną listę manipulatorów znajdziesz tutaj . Z tego łącza wszystkie flagi strumienia powinny mówić ustawione do czasu zmiany przez innego manipulatora. Jedna uwaga na temat
left
,right
iinternal
manipulatory są jak inne flagi i nie utrzymują się aż zmieniło. Jednak mają one wpływ tylko wtedy, gdy ustawiona jest szerokość strumienia, a szerokość musi być ustawiana w każdym wierszu. Na przykładdałby ci
ale
dałby ci
Manipulatory wejścia i wyjścia nie są lepkie i pojawiają się tylko raz, gdy są używane. Sparametryzowane manipulatory są różne, oto krótki opis każdego z nich:
setiosflags
pozwala ręcznie ustawić flagi, których listę można znaleźć tutaj , więc jest lepka.resetiosflags
zachowuje się podobnie dosetiosflags
z wyjątkiem tego, że usuwa określone flagi.setbase
ustawia podstawę liczb całkowitych wstawionych do strumienia (więc 17 w bazie 16 będzie równe „11”, a podstawą 2 będzie „10001”).setfill
ustawia znak wypełnienia do wstawienia do strumienia, gdysetw
jest używany.setprecision
ustawia dokładność dziesiętną, która ma być używana podczas wstawiania wartości zmiennoprzecinkowych.setw
sprawia, że tylko następne wstawienie ma określoną szerokość, wypełniając znak określony wsetfill
źródło
std::hex
też nie jest lepki i, oczywiście,std::flush
czystd::setiosflags
nie są lepkie albo. Więc nie sądzę, że to takie proste.std::hex
nie jest lepka, była błędna - ja też się o tym dowiedziałem. Flagi strumienia mogą się jednak zmienić, nawet jeśli nie wstawiszstd::setiosflags
ponownie znaku, więc można to uznać za nieprzywierające. Niestd::ws
jest też lepki. Nie jest to więc takie proste.