Why is #include <string> preventing a stack overflow error here?

C++StringStack OverflowExplicit

C++ Problem Overview


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.

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionairborneView Question on Stackoverflow
Solution 1 - C++Pavel PView Answer on Stackoverflow
Solution 2 - C++cbuchartView Answer on Stackoverflow