|
Prev: What the program would behave if an exception is thrown in an environment with exception disabled
Next: Requiring client code to provide implementation file.
From: Thomas Maeder on 5 Jan 2008 23:20 Andreas Magnusson <eamagnusson(a)gmail.com> writes: > I've tried the following code both with two different version of MSVC > (7.1 and 8) and GCC, all of them give the same (for me) strange > result. First the code: > > class sstream_derived : public std::stringstream The Standard C++ Library stream classes are not designed to serve as (non-private) base classes. What is the problem that you are trying to solve by deriving from std::stringstream? > { > public: > sstream_derived() > { > std::cout << "sstream_derived()" << std::endl; > } > sstream_derived(const sstream_derived &ssd) > { > std::cout << "sstream_derived(const sstream_derived &ssd)" << > std::endl; > } Stream objects are supposed to not being copyable, because it's very hard to find meaningful semantics for this operation. > virtual ~sstream_derived() > { > std::cout << "~sstream_derived: " << str() << std::endl; > } > }; > > class test > { > public: > sstream_derived get_stream() > { > return sstream_derived(); > } > }; > > int main() > { > test t; > std::string name = "Andreas"; > int age = 30; > // > t.get_stream() << "Hello, my name is " << name << ", I'm " << age << > " years old!"; The type of the expression t.get_stream() is sstream_derived, and the type of "Hello, my name is " char[19]. Both these types belong to the global namespace. When a matching operator<< is looked for, only members of sstream_derived and operator<< non-members that belong to the global namespace are considered; these include opeator<<(void *) (which is a member) and many arithmetic operator<<s; the operator<< overload for char const * is not considered since it is a non-member and belongs to namespace std. > t.get_stream() << 42 << "Hello, my name is " << name << ", I'm " << > age << " years old!"; The type of the expression t.get_stream() << 42 is std::ostream. This means that for the expression t.get_stream() << 42 << "Hello, my name is " operator<< non-members in namespace std are also considered, including the operator<< overload for char const *, which turns out to be the best match. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Andreas Magnusson on 7 Jan 2008 04:30 On 6 Jan, 17:01, int...(a)gmail.com wrote: > On 6 Jan, 02:34, Andreas Magnusson <eamagnus...(a)gmail.com> wrote: > > > > > I've tried the following code both with two different version of MSVC > > (7.1 and 8) and GCC, all of them give the same (for me) strange > > result. First the code: > > > > Anyone knows what's wrong? > > The problem has nothing to do with you inheriting from > std::stringstream (though why you'd want to is still a mystery), and Hehe, I'm mystery man. Nah, seriously I could just as well derive from std::ostream and construct it with a std::stringbuf which basically is what std::stringstream does. I just thought I'd save myself a new/ delete pair. > everything with the fact that operator<<(const void*) is defined as a > member in class std::basic_ostream, while operator<<(basic_ostream&, > const char*) is a global function in namespace std. Note the signature Thanks, that was the answer! > of the latter - the first argument is a reference to non-const. Now, > your get_stream() function returns an temporary object; and > temporaries do not bind to such references. On the other hand, all > member overloads for operator<< are perfectly valid for a temporary, > and so the only one of those matching the argument - const char* - is > selected, which is the const void* one. > > A better question is, why are you even trying to return a copy of a > stream from a function? Yeah, I agree. My idea was to output the complete stringstream in the dtor of sstream_derived. Thus I need the dtor to run, preferably before the end of the current block. By returning an rvalue I can get the desired functionality, except of course for the small fact that std::basic_ostream<> doesn't define a member overload for operator<<(const char *), which I didn't know, now I do. That's also the reason I want to derive from std::stringstream. I want all that std::stringstream has but I want to define my own dtor. Now you now all my secrets. Thanks a lot for your help! Andreas -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Andreas Magnusson on 7 Jan 2008 04:30 On 6 Jan, 17:05, "Daniel Kr�gler" <daniel.krueg...(a)googlemail.com> wrote: > On 6 Jan., 00:34, Andreas Magnusson <eamagnus...(a)gmail.com> wrote: > > First, I expect to see at least the following include's here: > > #include <sstream> > #include <ostream> > #include <iostream> Of course, the program wouldn't compile without them. I just thought I'd save some space by excluding lines completely unrelated to my question. > It seems that something nasty happens here ;-) > > You are providing a faking copy c'tor, because > the copy c'tor does not invoke the copy c'tor > of it's base class. Of-course, this would not > be possible, since the base class itself is > not copyable. Yeah, I know, I'm not using it but GCC wouldn't compile unless I had it and I compiled the code with GCC to see whether my problem was MSVC specific or not. It would've been nasty had I used it. > Is this construction by design? Yep. The magic is to do the output in the dtor. > > class test > > { > > public: > > sstream_derived get_stream() > > { > > return sstream_derived(); > > } > > }; > > The immediate source of your problem is > test::get_stream, which returns an rvalue > of sstream_derived. Now consider a shortend > version of your problem code: > > test().get_stream() << "Hello, my name is "; > > You seem to expect that this line should > output the character string "Hello, my name is " > but this is not possible, because the corresponding > inserter function need to be one of the the free > function templates from header <ostream>: > [snip] > template<class traits> > basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, > const char*); > > All of them expect an *lvalue* of basic_ostream<char,traits>, > which is not what you provide. Thanks, that's exactly what I was looking for. I have always thought that std::basic_ostream<> has overloaded <<-operators for *all* built- in types, but now I know it doesn't and I see why it would never work. > In C++0x this workaround is not needed du to the newly > introduced rvalue references and the movability of streams. That sounds nice. Thanks, Andreas -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Daniel Krügler on 8 Jan 2008 22:45
On 7 Jan., 22:30, Andreas Magnusson <eamagnus...(a)gmail.com> wrote: > On 6 Jan, 17:05, "Daniel Kr�gler" <daniel.krueg...(a)googlemail.com> > wrote: > > First, I expect to see at least the following include's here: > > > #include <sstream> > > #include <ostream> > > #include <iostream> > > Of course, the program wouldn't compile without them. I just thought > I'd save some space by excluding lines completely unrelated to my > question. I added the remark, because some astonishing effects indeed *can* be based on missing include headers, still leading to compilable code. One example is the following: In recent C++03 does *not* exist any explicit requirement that <iostream> already includes everything contained in <ostream>. You might wonder concerning this, but there are conforming implementations, which doesn't. The reason for this is, that the IO classes have to fulfill some hard to implement properties (e.g. even in the c'tor or d'tor of global static instances of a class std::cout and friends "have to work"), thus implementors usually have to use some heavy hackery tricks (which are granted by the standard for them) to realize these extraordinary requirements.[1] So the following very simple program - which has some similarities to yours - indeed can result in a very similar observation as in your case: #include <iostream> int main() { std::cout << "Hello World\n"; std::cout.flush(); } Because on some implementations the inserter for const char* is not declared here, the program would also chose the member inserter for const void*. Because of these subtilities with header dependencies I usually check them and mention missing ones and I strongly recommend to *always* provide a *complete* running program - especially in cases of an error analysis. > > It seems that something nasty happens here ;-) > > > You are providing a faking copy c'tor, because > > the copy c'tor does not invoke the copy c'tor > > of it's base class. Of-course, this would not > > be possible, since the base class itself is > > not copyable. > > Yeah, I know, I'm not using it but GCC wouldn't compile unless I had > it and I compiled the code with GCC to see whether my problem was MSVC > specific or not. It would've been nasty had I used it. Actually it is possible to simulate a *copy* of a stream, see e.g. Langer/Krefts book "Standard C++ IOStreams and Locales" for details. In short you need to - Clear the exception mask - Copy the stream state (rdstate) via clear - Assign the stream buffer (rdbuf) - Invoke copyfmt There still remain some subtle problems doing so, so for this reason streams are not copyable by design. [1]: There are attempts to define at least some explicit header dependencies for C++, see the still open issue: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#343 -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |