Bug oder nicht?
-
Ich habe mal ein Stück code debugged, das eine access violation auslöst. Sinngemäß war es so etwas:
#include <cassert> #include <fstream> std::ifstream OpenRead(const char* file) { std::ifstream stream(file, std::ios_base::in | std::ios_base::binary); if( !stream.good() ) throw "Unable to open file!"; stream.exceptions(std::ios_base::badbit); return stream; } int main() { static const char* filename = "main.cpp"; std::istream input = OpenRead(filename); // access violation //std::ifstream input = OpenRead(filename); // fixes the bug char test = input.get(); assert( test == '#' ); }
Also offensichtlich ein kleiner Flüchtigkeitsfehler, der nicht ganz leicht zu finden war. Jetzt würden mich mal konkret 2 Sachverhalte interessieren:
(1) Wenn man das durchstepped, macht er zunächst einen basic_ifstream(basic_ifstream&&) und anschließend einen basic_istream(basic_istream&&) move. Darf der Compiler hier überhaupt 2mal moven?
(2) Warum genau funktioniert der istream nicht mehr? Klar, er wurde gesliced, das ist im Normalfall nicht so gut, aber der streambuf sollte ja noch unverändert übernommen worden sein und ist polymorph. Oder ist da das Verhalten wirklich schon undefiniert?
-
Streams sollten nicht kopierbar sein. Wenn dein Code überhaupt compiliert würde ich das schon fast als Fehler in der zugehörigen Standardbibliothek ansehen.
-
Der Stream wird nicht kopiert, sondern verschoben, gemäß dem C++0x Standard. Das ist eine sehr feine Sache, da man Streams früher nicht aus Funktionen zurückgeben konnte, außer über Pointer.
Die Frage ist, was beim moven alles passieren darf und was nicht...
-
Dann müsste doch der Stream ein RValue-Referenz (&&) sein, oder?
-
Also g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5 meckert, weil der copy-ctor, so wie ich es auch erwarten würde, private ist:
g++ -std=c++0x main.cpp In file included from /usr/include/c++/4.4/ios:39, from /usr/include/c++/4.4/istream:40, from /usr/include/c++/4.4/fstream:40, from main.cpp:2: /usr/include/c++/4.4/bits/ios_base.h: In copy constructor ‘std::basic_ios<char, std::char_traits<char> >::basic_ios(const std::basic_ios<char, std::char_traits<char> >&)’: /usr/include/c++/4.4/bits/ios_base.h:790: error: ‘std::ios_base::ios_base(const std::ios_base&)’ is private /usr/include/c++/4.4/iosfwd:47: error: within this context /usr/include/c++/4.4/iosfwd: In copy constructor ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’: /usr/include/c++/4.4/iosfwd:81: note: synthesized method ‘std::basic_ios<char, std::char_traits<char> >::basic_ios(const std::basic_ios<char, std::char_traits<char> >&)’ first required here /usr/include/c++/4.4/streambuf: In copy constructor ‘std::basic_filebuf<char, std::char_traits<char> >::basic_filebuf(const std::basic_filebuf<char, std::char_traits<char> >&)’: /usr/include/c++/4.4/streambuf:770: error: ‘std::basic_streambuf<_CharT, _Traits>::basic_streambuf(const std::basic_streambuf<_CharT, _Traits>&) [with _CharT = char, _Traits = std::char_traits<char>]’ is private /usr/include/c++/4.4/iosfwd:78: error: within this context /usr/include/c++/4.4/iosfwd: In copy constructor ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’: /usr/include/c++/4.4/iosfwd:81: note: synthesized method ‘std::basic_filebuf<char, std::char_traits<char> >::basic_filebuf(const std::basic_filebuf<char, std::char_traits<char> >&)’ first required here main.cpp: In function ‘std::ifstream OpenRead(const char*)’: main.cpp:13: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’ first required here
-
Dobi schrieb:
Also g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5 meckert, weil der copy-ctor, so wie ich es auch erwarten würde, private ist
Hier geht's doch um C++0x, da kann man mit R-Value Referenzen das Objekt rüber-"move"n, es wird also nix kopiert.
@TO: Muss es nicht eigentlich
return std::move(stream);
sein? Undifstream
zu slicen halte ich für keine gute Idee, keine Ahnung warum deine Implementierung das erlaubt; weiß jemand ob das Standard-konform ist?
-
AFAIK macht das return statement den stream auch zu ner rvalue-Referenz. Und der move-Konstruktor wird ja auch benutzt (wie gesagt, *zwei* move ctors werden sogar benutzt). Ich hab's aber auch nochmal mit nem expliziten std::move compiliert. Es passiert genau das Gleiche.
Und ifstream zu slicen halte ich für keine gute Idee, keine Ahnung warum deine Implementierung das erlaubt; weiß jemand ob das Standard-konform ist?
Eine sehr gute Frage. Es geht übrigens um den VC10.
-
fdfdg schrieb:
Dobi schrieb:
Also g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5 meckert, weil der copy-ctor, so wie ich es auch erwarten würde, private ist
Hier geht's doch um C++0x
Ok, sorry. Habs grad in VC10E ausprobiert, und da knallts bei mir auch.
Hab beim googeln auch eben deinen Post bei connect.microsoft gefunden, den ich auch weiterverfolgen werde.
-
Gut, laut MS ist der Standard inzwischen abgeändert worden, so dass istream(istream&&) protected ist. Dadurch kann das slicen so nicht mehr stattfinden.