Search code examples
c++one-definition-rulec++-modules

Do C++ modules make ODR violations absent?


From the N4720 C++ Modules draft, [basic.def.odr]/6 says:

[…] For an entity with an exported declaration, there shall be only one definition of that entity; a diagnostic is required only if the abstract semantics graph of the module contains a definition of the entity. [Note: If the definition is not in the interface unit, then at most one module unit can have and make use of the definition. — end note] […]

Which, as per my understanding, practically gives templates a chance to be parsed only once by the compiler, contrasting to the current state of affairs (having an exact copy of their definitions per translation unit). This is also valid for other entities with similar case, such as inline functions/variables.

My question arised from the fact that, as you can only have at most one definition of entities (as described in [basic.def.odr]/1) per translation unit, it is undefined behavior to have differing definitions of an entity across TUs. And, as exported entities shall have only one definition across the entire compilation unit (making unexported ones unique to their implementation unit), getting the definition wrong, from my perspective, is harder if not impossible.

Finally, to put it simply: will (or does, or should) the utilization of modules make it impossible to violate ODR rules, or otherwise harder to get wrong?


Solution

  • If a project is fully modularized (that is, never uses #include), then most accidental ODR violations would go away. Most accidental ODR violations happen due to the nature of #include: including a global variable with a definition and so forth. Or two-phase template lookup issues, where two files include the same template, but because of what each included before that template, the definition of the two templates is different.

    However, this does nothing to prevent less "accidental" ODR violations. Because "same entity" is defined by its name, and the name of an entity has nothing to do with the module it was exported from, two modules can provide different definitions of the same entity. So basically, name collisions.

    This only becomes a compile-error (ie: diagnostic required) if a single translation unit imports both such modules. If two separate translation units each include one of the modules with different definitions, then the program as a whole still violates ODR, but there doesn't have to be a diagnostic about it.

    So even in a fully modularized codebase, ODR can still be violated.