Why is #include <string> preventing a stack overflow error here?
This is my sample code:
#include <iostream>
#include <string>
using namespace std;
class MyClass
{
string figName;
public:
MyClass(const string& s)
{
figName = s;
}
const string& getName() const
{
return figName;
}
};
ostream& operator<<(ostream& ausgabe, const MyClass& f)
{
ausgabe << f.getName();
return ausgabe;
}
int main()
{
MyClass f1("Hello");
cout << f1;
return 0;
}
If I comment out #include <string>
I don't get any compiler error, I guess because it's kind of included through #include <iostream>
. If I "right-click --> Go to Definition" in Microsoft VS they both point to the same line in the xstring
file:
typedef basic_string<char, char_traits<char>, allocator<char> >
string;
But when I run my program, I get an exception error:
0x77846B6E (ntdll.dll) in OperatorString.exe: 0xC00000FD: Stack overflow (Parameter: 0x00000001, 0x01202FC4)
Any idea why I get a runtime error when commenting out #include <string>
? I'm using VS 2013 Express.
Indeed, very interesting behavior.
Any idea why I get I runtime error when commenting out #include <string>
With MS VC++ compiler the error happens because if you do not #include <string>
you won't have operator<<
defined for std::string
.
When the compiler tries to compile ausgabe << f.getName();
it looks for an operator<<
defined for std::string
. Since it was not defined, the compiler looks for alternatives. There is an operator<<
defined for MyClass
and the compiler tries to use it, and to use it it has to convert std::string
to MyClass
and this is exactly what happens because MyClass
has a non-explicit constructor! So, the compiler ends up creating a new instance of your MyClass
and tries to stream it again to your output stream. This results in an endless recursion:
start:
operator<<(MyClass) ->
MyClass::MyClass(MyClass::getName()) ->
operator<<(MyClass) -> ... goto start;
To avoid the error you need to #include <string>
to make sure that there is an operator<<
defined for std::string
. Also you should make your MyClass
constructor explicit to avoid this kind of unexpected conversion. Rule of wisdom: make constructors explicit if they take only one argument to avoid implicit conversion:
class MyClass
{
string figName;
public:
explicit MyClass(const string& s) // <<-- avoid implicit conversion
{
figName = s;
}
const string& getName() const
{
return figName;
}
};
It looks like operator<<
for std::string
gets defined only when <string>
is included (with the MS compiler) and for that reason everything compiles, however you get somewhat unexpected behavior as operator<<
is getting called recursively for MyClass
instead of calling operator<<
for std::string
.
Does that mean that through #include <iostream>
string is only included partly?
No, string is fully included, otherwise you wouldn't be able to use it.
The problem is that your code is doing an infinite recursion. The streaming operator for std::string
( std::ostream& operator<<(std::ostream&, const std::string&)
) is declared in <string>
header file, although std::string
itself is declared in other header file (included by both <iostream>
and <string>
).
When you don't include <string>
the compiler tries to find a way to compile ausgabe << f.getName();
.
It happens that you have defined both a streaming operator for MyClass
and a constructor that admits a std::string
, so the compiler uses it (through implicit construction), creating a recursive call.
If you declare explicit
your constructor ( explicit MyClass(const std::string& s)
) then your code won't compile anymore, since there is no way to call the streaming operator with std::string
, and you'll be forced to include the <string>
header.
EDIT
My test environment is VS 2010, and starting at warning level 1 ( /W1
) it warns you about the problem:
warning C4717: 'operator<<' : recursive on all control paths, function will cause runtime stack overflow
链接地址: http://www.djcxy.com/p/24570.html