#include <sstream>
#include <iostream>
#include <string>
class test : public std::stringstream
{
public:
void addString(const char* str);
};
void test::addString(const char* str)
{
(*this) << "Test: " << str;
}
int main()
{
test* t = new test();
t->addString("a string");
std::cout << t->str();
delete t;
return(0);
}
When this code is run, inside the test::addString method, the stream
insertion operator is called for the string "Test:". This *should* invoke
the overload for (const char *). However, it actually invokes the overload
for (void *) causing the address of the string to be printed out instead.
If we move this functionality directly into the main function like so:
int main()
{
test* t = new test();
(*t) << "Test: " << "a string";
std::cout << t->str();
delete t;
return(0);
}
The compiler correctly identifies (const char *) as the proper overload for
the string "Test: " and the string prints out. As further evidence that
something is wrong, if the function test::addString is changed to:
void test::addString(const char* str)
{
std::string theString("Test: ");
(*this) << theString << str;
}
The code will not compile and it displays an error that no valid method for
type std::string can be found for the insertion operator. However, the code
will compile cleanly if the same thing is placed into main like this:
int main() {
test* t = new test();
std::string aString("Test: ");
(*t) << aString << "a string";
std::cout << t->str();
delete t;
return(0);
}
The final proof that the complier is confused came when we added an explicit
cast in test::addString to give:
void test::addString(const char* str) {
(static_cast<std::stringstream&>(*this)) << "Test: " << str;
}
With the cast in place, the complier properly maps "Test: " as a call to the
(const char*) overload, and requests to pass a std::string into the
insertion operator compile successfully.
Somehow, the use of (*this) inside a method of our class is not being
handled properly by the compiler. Has anyone else noticed this?
Cliff
<cliff_mccollum at hotmail dot com>
>We believe we have found a compiler error in C++ .NET 2003 using the
>supplied STL. In summary, the compiler incorrectly identifies the type of a
>subclass and fails to invoke the correct function overload. Here is the code
>that demonstrates the problem:
>
> #include <sstream>
> #include <iostream>
> #include <string>
Strictly speaking, to make it standard and portable, you need:
#include <ostream>
>
> class test : public std::stringstream
> {
> public:
> void addString(const char* str);
> };
>
> void test::addString(const char* str)
> {
> (*this) << "Test: " << str;
> }
>
> int main()
> {
> test* t = new test();
> t->addString("a string");
> std::cout << t->str();
> delete t;
>
> return(0);
> }
>
>When this code is run, inside the test::addString method, the stream
>insertion operator is called for the string "Test:". This *should* invoke
>the overload for (const char *). However, it actually invokes the overload
>for (void *) causing the address of the string to be printed out instead.
I agree that it should invoke the (const char*) overload, but the
reason for this is quite complex.
The problem appears to be that VC7 is failing to perform ADL properly
on the non-member operator set of a function when the member operator
exists in a base class. Here's what's meant to happen:
void test::addString(const char* str)
{
(*this) << str;
}
The expression "(*this) << str" is an operator expression. Operator
expressions have three forms of name lookup - member operator lookup,
non-member operator lookup and built in operator lookup. Here's what
should happen for the three:
Member operator lookup:
(*this).operator<<(str) is checked. std::basic_ostream::operator<< is
found.
Non-member operator lookup:
operator<<(*this, str) is checked. Ordinary lookup finds nothing -
there are no operator<< functions at all in the global namespace.
Next, ADL is invoked. The namespace set of *this is that of itself
(the global namespace) plus that of its base classes (namespace
std)*****. The namespace set of str is empty, since it is a built-in.
So namespace std is searched for operator<<, and sure enough, some
candidate functions are found.
Built-in operator lookup:
No candidate functions are found.
The candiate functions are therefore:
All ostream::operator<< functions (including the void* one).
All std::operator<< functions so far declared (in particular, those in
<ostream> have been declared). (including the char const* one).
Obviously, std::operator<<(ostream&, char const*) is the best match,
and should be chosen unambiguously.
Now, MSVC7 only has the member ones to choose from because of the bug,
so the void* one is chosen as the best match.
In general VC7.1 has good name lookup skills (too good at times
(compared to what the standard requires) - they use fully recursive
searching of template parameter namespaces usually). However, I agree
with you that this is a bug.
Tom
***** This seems to be the mistake that MSVC7.1 is making - it isn't
searching the namespaces of base classes. This is why it works when
the cast to std::stringstream.
Thanks to tom (see his post) for an excellent analysis. I agree that this
is a bug. You can consider it reported, so it should be fixed in a future
release.
-cd
Cliff
"Carl Daniel [VC++ MVP]" <cpda...@nospam.mvps.org> wrote in message
news:OKBQArrf...@TK2MSFTNGP11.phx.gbl...