Rules Warning

I was bitten by the unpleasant "one rule of definition." I am now afraid to have many subtle mistakes in my projects.

For example, the following program will dereference a null pointer 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 without warning.

This is 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 are no more problems.

In large projects, it seems very difficult to ensure that not a single cpp file locally defines a structure or class with an already defined name.

I have some classes, such as Point , Serie , State , Item , ... I, although this was normal in small cpp files, but I understand that it is not safe.

Is there a compiler warning to catch such errors? If not, what are the best methods to avoid breaking ODR?

+7
c ++ visual-c ++ visual-studio
source share
3 answers

In this particular case, at the very bottom, the ODR violation (which actually leads to the problem you are observing) is an implicitly built-in constructor of class S Your program has two inappropriate versions of the built-in function S::S() , which can be considered as another ODR violation caused by the original ODR violation (i.e., the 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, this can be done with sufficient effort.

In this case, to make the error "visible", you can explicitly declare and define the constructor of the class as a non-built-in function with an empty body. Having two non-embedded S::S() will result in a linker error.

It is clear that you can consider this as too artificial a measure, unacceptable in some cases, since it can change the "aggregate" status of the class.

+3
source share

If not, what are the best methods to avoid breaking ODR?

This is fundamental why we have namespaces.

Use one well-known namespace for each program component (e.g. boost , std , asio , sql , mytool , yourlib , etc.).

The namespace is actually part of its name, so the following:

 namespace X { struct S {}; } namespace Y { struct S {}; } struct S {}; 

leads to the definition of three different classes. One is called X::S , one is called Y::S , and the other is S , also known as ::S

:: is a global namespace. Preventing name declarations here is a good idea, since any C components that you use in your program (or naively written C ++ components) quickly pollute this namespace with your names.

+4
source share
  • it’s easy to organize class / structure names, even with millions of codes, with a powerful namespace . Remember that namespace can define nested levels.

  • if you really want some kind of definition "locally" try anonymous namespace

  • I remember that the standard clearly does not require any diagnostics if the programmer violates ODR, so rely on yourself.

+3
source share

All Articles