Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 any compiler or linker errors. But the output is strange for me:

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

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

  • same as [EG1] but with -O2 option enabled [EG2]:

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

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

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

  • same as [EG3] but with /O2 (or /Ox) enabled [EG4]:

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

I would expect either a linker error (assuming ODR rule is violated) or the output as in [EG2] (code is inlined, nothing is exported from the translation unit, ODR rule is held). Thus my questions:

  1. Why are outputs [EG1], [EG3], [EG4] possible?
  2. Why do I get different results from different compilers or even from the same compiler? That makes me think that the standard somehow doesn't specify the behaviour in this case.

Thank you for any suggestions, comments and standard interpretations.

Update
I would like to understand the compiler's behaviour. More precisely, why there are no errors generated if the ODR is violated. A hypothesis is that since all functions in classes UsedClass1 and UsedClass2 are marked as inline (and therefore C++03 3.2 is not violated) the linker doesn't report errors, but in this case outputs [EG1], [EG3], [EG4] seem strange.

like image 396
user1221434 Avatar asked Feb 20 '12 16:02

user1221434


3 Answers

This is the rule that prohibits what you're doing (the C++11 wording), from section 3.2 of the Standard:

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

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

  • in each definition of D, corresponding names, looked up according to 3.4, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (13.3) and after matching of partial template specialization (14.8.3), except that a name can refer to a const object with internal or no linkage if the object has the same literal type in all definitions of D, and the object is initialized with a constant expression (5.19), and the value (but not the address) of the object is used, and the object has the same value in all definitions of D; and

  • in each definition of D, corresponding entities shall have the same language linkage; and

  • in each definition of D, the overloaded operators referred to, the implicit calls to conversion functions, constructors, operator new functions and operator delete functions, shall refer to the same function, or to a function defined within the definition of D; and

  • in each definition of D, a default argument used by an (implicit or explicit) function call is treated as if its token sequence were present in the definition of D; that is, the default argument is subject to the three requirements described above (and, if the default argument has sub-expressions with default arguments, this requirement applies recursively).

  • if D is a class with an implicitly-declared constructor (12.1), it is as if the constructor was implicitly defined in every translation unit where it is odr-used, and the implicit definition in every translation unit shall call the same constructor for a base class or a class member of D.

In your program, you're violating the ODR for class UsedClass because the tokens are different in different compilation units. You could fix that by moving the definition of UsedClass::doit() outside the class body, but the same rule applies to the body of inline functions.

like image 84
Ben Voigt Avatar answered Nov 08 '22 19:11

Ben Voigt


Your program violates the One Definition Rule and invokes an Undefined Behavior.
The standard does not mandate an diagnostic message if you break the ODR but the behavior is Undefined.

C++03 3.2 One definition rule

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

Every program shall contain exactly one definition of every non-inline function or object that is used in that program; no diagnostic required. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and 12.8). An inline function shall be defined in every translation unit in which it is used.

Further the standard defines specific requirements for existence of multiple definitions of an symbol, those are aptly defined in Para #5 of 3.2.

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

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

like image 24
Alok Save Avatar answered Nov 08 '22 18:11

Alok Save


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

The simple answer is that the behaviour is undefined, so anything is possible.

Most compilers handle an inline function by generating a copy in each translation unit in which it's defined; the linker then arbitrarily chooses one to include in the final program. This is why, with optimisations disabled, it calls the same function in both cases. With optimisations enabled, the function might be inlined by the compiler, in which case each inlined call will use the version defined in the current translation unit.

That makes me think that the standard somehow doesn't specify the behaviour in this case.

That's correct. Breaking the one definition rule gives undefined behaviour, and no diagnostic is required.

like image 21
Mike Seymour Avatar answered Nov 08 '22 20:11

Mike Seymour