C ++: different classes with the same name in different translation units

Consider the following example:

// usedclass1.hpp #include <iostream> class UsedClass { public: UsedClass() { } void doit() { std::cout << "UsedClass 1 (" << this << ") doit hit" << std::endl; } }; // usedclass2.hpp #include <iostream> class UsedClass { public: UsedClass() { } void doit() { std::cout << "UsedClass 2 (" << this << ") doit hit" << std::endl; } }; // object.hpp class Object { public: Object(); }; // object.cpp #include "object.hpp" #include "usedclass2.hpp" Object::Object() { UsedClass b; b.doit(); } // main.cpp #include "usedclass1.hpp" #include "object.hpp" int main() { Object obj; UsedClass a; a.doit(); } 

The code compiles without compiler or linker errors. But the output is strange for me:

  • gcc (Red Hat 4.6.1-9) on Fedora x86_64 without optimization [ EG1 ]:

    UsedClass 1 (0x7fff0be4a6ff) doit hit
    UsedClass 1 (0x7fff0be4a72e) doit hit

  • the same as [EG1], but with the -O2 [ EG2 ] option turned on:

    UsedClass 2 (0x7fffcef79fcf) UseClass 1 (0x7fffcef79fff) doit hit

  • msvc2005 (14.00.50727.762) on Windows XP 32bit without optimization [ EG3 ]:

    UsedClass 1 (0012FF5B) UsedClass 1 (0012FF67) doit hit

  • similar to [EG3], but with / O 2 (or / Ox) included [ EG4 ]:

    UsedClass 1 (0012FF73) doit hit
    Used Class 1 (0012FF7F) doit hit

I expect either a linker error (assuming that the ODR rule is violated) or the result, as in [EG2] (the code is embedded, nothing is exported from the translation unit, the ODR rule is saved). So my questions are:

  • Why are outputs [EG1], [EG3], [EG4] possible?
  • Why do I get different results from different compilers or even from the same compiler? This makes me think that the standard somehow does not define behavior in this case.

Thank you for any suggestions, comments and standard interpretations.

Update
I would like to understand the behavior of the compiler. More precisely, why there are no errors that occur when ODR is violated. The hypothesis is that since all functions in the UsedClass1 and UsedClass2 classes are marked as inline (and therefore C ++ 03 3.2 is not violated), the linker does not report errors, but in this case outputs [EG1], [EG3], [EG4 ] seem strange.

+6
source share
3 answers

This rule prohibits what you do (C ++ 11 wording) from section 3.2 of the standard:

There can be several class type definitions (section 9), an enumeration type (7.2), a built-in function with external communication (7.1.2), a class template (section 14), a non-static function template (14.5.6), a static data element of a class template ( 14.5.1.3), a member function of a class template (14.5.1.1), or a specialized specialization of a template for which any template parameters are not specified (14.7, 14.5.5) in the program, provided that each definition appears in a different translation unit and provided that the definitions satisfy the following requirements. Given such an object named D , defined in more than one translation unit, then

  • each definition of D must consist of the same sequence of tokens ; and

  • in each definition of D , the corresponding names scanned in accordance with 3.4 refer to the entity defined in definition D , or must refer to the same object after resolving the overload (13.3) and after matching the partial specialization (14.8.3), except that the name can refer to a const object with an internal or missing binding if the object has the same literal type in all D definitions and the object is initialized with the constant expression (5.19) and the value (but not the address) of the object is used project, and the object has the same meaning in all definitions of D ; and

  • in each definition of D , the corresponding objects must have the same language connection; and

  • in each definition of D , the overloaded referenced operators, implicit calls to conversion functions, constructors, new operator functions, and operator removal functions must refer to the same function or to the function defined inside the definition of D; and

  • in each definition of D , the default argument used by an (implicit or explicit) function call is treated as if its token sequence was present in the definition of D ; that is, the default argument obeys the three requirements described above (and if the default argument has subexpressions with the default arguments, this requirement is applied recursively).

  • if D is a class with an implicit declaration of the constructor (12.1), it is as if implicitly defined in each translation block where it is used odr, and the implicit definition in each translation, the unit should call the same constructor for the base class or member of the class D

In your program, you violate the ODR for class UsedClass because the tokens are different in different compilation units. You can fix this by moving the UsedClass::doit() definition outside the class body, but the same rule applies to the body of built-in functions.

+12
source

Your program violates the rule of one definition and invokes Undefined behavior.
The standard does not provide a diagnostic message if you violate ODR, but the behavior is Undefined.

C ++ 03 3.2 One definition rule

No translation unit should contain more than one definition of any variable, function, type of class, type of enumeration or template ....

Each program must contain exactly one definition of each non-built-in function or object that is used in this program; no diagnostics required. The definition can be explicitly displayed in the program, it can be found in the standard or user-defined library, or (if necessary), it is implicitly defined (see 12.1, 12.4 and 12.8). A built-in function must be defined in each translation unit in which it is used.

The standard further defines the specific requirements for the existence of a variety of symbol definitions, which are precisely defined in paragraph 5 3.2.

There can be more than one definition of a class type (section 9), an enumeration type (7.2), a built-in function with external communication (7.1.2), a class template (section 14), a non-static function template (14.5.5), a static data member of a class template (14.5.1.3), a member function of a class template (14.5.1.1) or a specialized specialization of a template for which some template parameters are not specified (14.7, 14.5.4) in the program, provided that each definition appears in a different translation unit and when provided that the definitions satisfy the following requirements. Given such an object named D, defined in more than one translation unit, then

- each definition of D should consist of the same sequence of tokens; and...

+7
source

Why are outputs [EG1], [EG3], [EG4] possible?

The simple answer: undefined behavior, so anything is possible.

Most compilers handle the built-in function, creating a copy in each translation unit in which it is defined; the linker then randomly selects the one to be included in the final program. That's why when optimizations are disabled, it calls the same function in both cases. When optimization is enabled, the function can be integrated by the compiler, in which case each built-in call will use the version defined in the current translation unit.

This makes me think that the standard somehow does not define behavior in this case.

It is right. Violation of one definition rule gives undefined behavior, and no diagnostics are required.

+4
source

Source: https://habr.com/ru/post/908908/


All Articles