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

120

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++
string
stack-overflow
explicit
asked on Stack Overflow May 2, 2017 by airborne • edited May 5, 2017 by cbuchart

2 Answers

161

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.

answered on Stack Overflow May 2, 2017 by Pavel • edited May 6, 2017 by Pavel
34

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

answered on Stack Overflow May 2, 2017 by cbuchart • edited May 23, 2017 by Community

User contributions licensed under CC BY-SA 3.0