Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One definition rule warning

I have been bitten by a nasty "one definition rule" violation. I am now afraid of having lots of subtle bugs in my projects.

For example, the following program will result in a null pointer dereference with visual studio 2015:

Source1.cpp:
----------
struct S {
    double d = 0;
};

void Foo() {
    S s;
}


Source2.cpp:
-----------
struct S {
    int a = 0;
};

int main() {

    int value = 5;
    int& valueRef = value;
    S s;           // valueRef is erased due to S::d initialization from Source1.cpp

    valueRef++;    // crash
}

This compiles with no warnings.

It's nasty because Source2.cpp doesn't even use anything from Source1.cpp. If I remove Source1.cpp from the project, it still compiles, and there is no problem anymore.

In large projects, it seems very hard to ensure that no cpp file "locally" define a struct or class with an already defined name.

I have some classes such as Point, Serie, State, Item, ... I though this was OK in small cpp files, but I realize it's not safe.

Is there a compiler warning to catch such bugs ? If not, what are the best practices to avoid ODR violation ?

like image 478
ThreeStarProgrammer57 Avatar asked Nov 17 '16 15:11

ThreeStarProgrammer57


People also ask

Which one is the correct description of the one definition rule?

The One Definition Rule (ODR) is an important rule of the C++ programming language that prescribes that objects and non-inline functions cannot have more than one definition in the entire program and template and types cannot have more than one definition by translation unit.

What does ODR-used mean?

10. +1 for the succinct opening sentence: "In plain word, odr-used means something(variable or function) is used in a context where the definition of it must be present."

What is ODR violation?

If an object, a reference or a function is odr-used, its definition must exist somewhere in the program; a violation of that is usually a link-time error. struct S { static const int x = 0; // static data member // a definition outside of class is required if it is odr-used }; const int& f(const int& r); int n = b ? (


2 Answers

cppcheck will detect ODR violations, and can be integrated into Visual Studio.

Here's a quick command line invocation using cppcheck ver 2.6 to find an ODR:

cppcheck classes/config/foo.cpp

Output:

classes/config/foo.h:15:1: error: The one definition rule is violated, different classes/structs have the same name 'MyClass' [ctuOneDefinitionRuleViolation]
class MyClass
^
classes/config/foo.h:60:1: note: The one definition rule is violated, different classes/structs have the same name 'MyClass'
class MyClass
^
classes/config/foo.h:15:1: note: The one definition rule is violated, different classes/structs have the same name 'MyClass'
class MyClass
^

Note that properly catching all ODRs requires the ability to see the full application source at once--full program analysis. So, cppcheck and other static analyzers may give false positives or miss some violations. However, this is an easy FOSS way to hunt for possibly problematic program constructs.

like image 58
Shaun Case Avatar answered Oct 01 '22 20:10

Shaun Case


In this particular case, at the very bottom the ODR violation (which actually leads to the problem you are observing) is the implicitly-defined inline constructor of class S. Your program has two non-matching versions of inline S::S() function, which can be seen as another ODR violation induced by the original ODR violation (i.e. same class defined differently).

It would be difficult for the implementation to "see" this error in the current approach to the C++ compilation infrastructure. Of course, it is possible to do with sufficient effort.

In this case in order to make the error "visible" you can explicitly declare and define the class constructor as a non-inline function with empty body. Presence of two non-inline S::S() will trigger a linker error.

Understandably, you might see this as an overly artificial measure, unacceptable in some cases since it might change the "aggregate" status of the class.

like image 25
AnT Avatar answered Oct 01 '22 22:10

AnT