1

I have a large C++11 project with MS Visual Studio 2015, that defines in two modules (= compilation units, cpp-files) two structs with same name but different content. Since the stucts are only defined and used within the modules and not exported for shared use via any header file, this should be allowed, and indeed neither the compiler nor the linker reports any error or warning. But at run time, I get an access violation from the constructor of an unordered map that contains the struct. The problem does also occur with Visual Studio 2017, but NOT with gcc5.4. In my opinion it is a compiler bug, but I am not absolutely sure. Here is some minimized source code to reproduce the problem, just link with any executable to get the access violation during startup before main().

module1.cpp:

#include <unordered_map>
struct AmbigousStruct {
    int i1;
    int i2;
    int i3;
};
static const std::unordered_map<int, AmbigousStruct>
s_ambigousStructMap{
    { 0, { 0, 1, 2 } }
};

module2.cpp:

#include <unordered_map>
struct AmbigousStruct {
    int i1;
    int i2;
};
static const std::unordered_map<int, AmbigousStruct>
s_ambigousStructMap{
    { 0, { 0, 1 } }
};

The problems seems to be related to using the struct in a template class (unordered_map in this case), since it does not occur with a simple instance of the struct, i.e. module2.cpp:

static const AmbigousStruct s_ambigousStructInstance{ 0, 1 };
Stefan
  • 83
  • 5

2 Answers2

1

You have two class types that have external linkage, with the same name, defined in different translation units. Their members don't match, and it's a One Definition Rule violation. Your program is ill-formed, no diagnostic required.

You need to force internal linkage on that struct definition. The C++ standard allows you to do that with an anonymous namespace. In fact, an anonymous namespace makes everything within it have internal linkage. So you don't need a static modifier for other things:

namespace {
  struct AmbigousStruct {
    int i1;
    int i2;
  };
  const std::unordered_map<int, AmbigousStruct>
  s_ambigousStructMap {
    { 0, { 0, 1 } }
  };
}
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
0

That's a linker problem, not a compiler bug. The linker will take the 1st definition that is found and ignores the 2nd one.

To solve the problem use a unnamed namespace for any stuff that's only locally used in a translation unit:

namespace { // Everything contained is only visible in the current 
            // translation unit
    struct AmbigousStruct {
        int i1;
        int i2;
        int i3;
    };
    const std::unordered_map<int, AmbigousStruct> s_ambigousStructMap {
        { 0, { 0, 1, 2 } }
    };
}

same in the other .cpp file.

user0042
  • 7,917
  • 3
  • 24
  • 39
  • Of course I know that the problem can be solved with an anonymous namespace. But the problem remains, when module1 and module2 are delivered by different developers of a large project. Both modules are not illformed for themselves, the compiler has no chance to find any problem since it compiles each module independently. In my mini example, the bug is easy to find since the exception occurs immediately on startup, but consider a more complex example where it may occur only in rare code branches. – Stefan Oct 29 '17 at 23:05
  • Why do I get no linker error or at least a warning if the code is really wrong? And why does it work without any problems gcc5.4? – Stefan Oct 29 '17 at 23:24