Search code examples
c++segmentation-faultcygwin

Why is std::basic_string<char> segfaulting on my overloaded stream operator?


I'm trying to compile and run this harmless looking class in g++ but the program keeps segfaulting either on malloc or memmove. There is hardly anything going on here.

I receive the following error when compiling with -g and running it through gdb. This is the extent of what I know on how to debug this.

For some reason this only segfaults when compiled from two separate files. If I copy/paste everything into main.cpp it runs fine.

Windows/Cygwin g++ (GCC) 4.9.2:

Program received signal SIGSEGV, Segmentation fault.

0x61137eb5 in memmove () from /usr/bin/cygwin1.dll

..

Program received signal SIGSEGV, Segmentation fault.

0x610ef417 in muto::acquire(unsigned long) () from /usr/bin/cygwin1.dll

Windows/g++ 4.9.2-TDM:

Program received signal SIGSEGV, Segmentation fault.

0x777d27d0 in ntdll!RtlCompareMemoryUlong () from /c/Windows/SysWOW64/ntdll.dll

Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7279ef5 in _int_malloc () from /lib64/libc.so.6 Missing separate debuginfos, use: debuginfo-install glibc-2.17-55.el7_0.5.x86_64 libgcc-4.8.2-16.2.el7_0.x86_64 libstdc++-4.8.2-16.2.el7_0.x86_64 ..

CentOS 7/g++ (GCC) 4.8.2:

Program received signal SIGSEGV, Segmentation fault.

_int_malloc (av=0x7ffff75b6760 , bytes=29) at malloc.c:3281

3281 {

I'm compiling using the following flags:

-pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wlogical-op -Wmissing-declarations -Wmissing-include-dirs -Wnoexcept -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-null-sentinel -Wstrict-overflow=5 -Wswitch-default -Wundef -Wno-unused

What am I missing? Is there anything else I can do in gdb to help pinpoint the problem?

$ g++ main.cpp string_t.cpp -g -std=c++11 $DEBUG -o test.exe && ./test.exe

main.cpp

#include <iostream>

using std::cout;
using std::endl;

#include "string_t.h"

int main() {
   string_t a("test");

   cout << a << endl;

   return 0;
}

string_t.h

#include <cstdint>
#include <string>

class string_t {
std::basic_string<char> _base;

public:
   string_t(
      const char* s);

   friend std::ostream& operator<<(
      std::ostream& l,
      const string_t& r);
};

string_t.cpp

#include "string_t.h"

string_t::string_t(
   const char* s) :
   _base(s) {
}

std::ostream& operator<<(
   std::ostream& l,
   const string_t& r)
{
   l << r._base.c_str();

   return l;
}

Solution

  • You did not include <ostream> into the second translation unit (string_t.cpp), so overload resolution fails to find

    template<class CharT, class Traits>
    basic_ostream<CharT,Traits>& operator << (basic_ostream<CharT,Traits>& os, const char* s);
    

    when you invoke

    l << r._base.c_str();
    

    Instead, string_t(const char* s) is used as a conversion constructor (it is not declared explicit) to create a new string_t object and

    std::ostream& operator<<(std::ostream& l, const string_t& r)
    

    is called again, so you end up with infinite recursion.