Question

When I declare a member variable of type std::map in one compilation unit but not the other, I get a segmentation fault when the containing object is being destructed. When I do the same with std::vector, it works just fine.

It was definitely a bug in my case, and I fixed it, but I'm still wondering what's causing the crash.

Here's the code:

foo.hpp:

#ifdef DECLARE_MAP
#include <map>
#endif
#ifdef DECLARE_VECTOR
#include <vector>
#endif
#include <string>

class Foo {
public:
    Foo();

private:
#ifdef DECLARE_MAP
    std::map<std::string, std::string> m;
#endif
#ifdef DECLARE_VECTOR
    std::vector<std::string> v;
#endif
};

foo.cpp:

#include "foo.hpp"

Foo::Foo()
{
}

main.cpp:

#include "foo.hpp"

int main()
{
    Foo f;
}

Works fine with DECLARE_VECTOR:

g++ -DDECLARE_VECTOR -c -o foo.o foo.cpp
g++ -o main main.cpp foo.o

But causes a segmentation fault with DECLARE_MAP:

g++ -DDECLARE_MAP -c -o foo.o foo.cpp
g++ -o main main.cpp foo.o

Reproducible in clang 4.0 and gcc 4.4.7.

Can anybody explain why this happens?

Was it helpful?

Solution 2

You're violating the One Definition Rule, resulting in Undefined Behaviour. This means literally anything can happen. Which includes working for some types involved and not others, or working only when the moon is full.

OTHER TIPS

The problem is because you're doing the compilation in two steps and only in the first step do you define DECLARE_MAP or DECLARE_VECTOR. This results in two translation units that look like this:

  • foo.cpp translation unit:

    // Contents of <map>
    // Contents of <string>
    
    class Foo {
    public:
        Foo();
    
    private:
        std::map<std::string, std::string> m;
    };
    
    int main()
    {
        Foo f;
    }
    
  • main.cpp translation unit:

    // Contents of <map>
    // Contents of <string>
    
    class Foo {
    public:
        Foo();
    
    private:
    };
    
    int main()
    {
        Foo f;
    }
    
    int main()
    {
        Foo f;
    }
    

As you can see, each translation unit has a different definition of Foo. The first has Foo containing a std::map and the second has it without.

This violates the following rule:

There can be more than one definition of a class type [...] in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then

  • each definition of D shall consist of the same sequence of tokens; and

  • [...]

If the definitions of D do not satisfy these requirements, then the behavior is undefined.

As you can see, you have undefined behaviour. Yes, it might appear to work when you have DECLARE_VECTOR, but that is only by chance. It still has undefined behaviour, so anything can happen.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top