Long explanation follows, question at bottom.
My question specifically refers to the current C++ draft standard (but also the current 'main' standard) found here. More specifically, section 3.2 point 6 (page 35) states each definition of D shall consist of the same sequence of tokens
, with regard to member functions and the ODR.
I recently encountered the following problem in a project while I was adding a new data analysis.
I was writing a file, A.cpp. I created a small dummy struct to hold some Data. In this example, I will call it Data
.
namespace Example {
struct Data {
//etc
};
//Use Data
};
However in anther file, B.cpp, there was already a struct called Data
inside the Example
namespace. The compiler generates Data::~Data();
for both classes, which in turn calls the destructors of their respective members. The definition in B.cpp contains a vector, which when destructed caused explosions when called on Data
structs using the layout defined in A.cpp. While both structs appear to work correctly, with no compile time errors, it appears that at link time the linker would pick one definition and use that, ignoring the other definition. (Hence caused explosions on Data
objects inside of A.cpp)
No warning is issued under GCC or under MSVC. When optimisation is enabled the problem does not occur (the functions are inlined, no link time confusion).
My question is, the standard only states that the behaviour is undefined If D is a template and is defined in more than one translation unit
.
Either I have misunderstood the standard, and the undefined behaviour is allowed to silently occur; or both GCC and MSVC are silently accepting something they shouldn't (and should either refuse to produce an output or issue a warning) (The current situation is undefined and inconsistent behaviour without a diagnostic).
Could someone please help me understand how this is different to conflicting definitions of functions that are not defined in classes (which do cause warnings/errors).
The difference is that function definitions inside a class are implicitly nominally inline, which inhibits the compiler warnings if the function's encountered again. That doesn't mean the compiler has to inline them - it may decide using whatever heuristics not to bother, or it may simple never inline at some optimisation levels. Anyway, if you link code that's seen different definitions of nominally inline non-member functions you have exactly the same problem.
See 3.2/6
More generally, you should have put your code into anonymous namespaces... they're designed to prevent cross-translation-unit problems of this kind.