There are several statements in the "3.2 Ondefinition rule" section of the standard, that covers different aspects of the rule.
- No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, or template.
This one covers the class type definition in your question, but this only applies to one translation unit (or source file). The same class type definition in the header included in different files (translations units) does not violate this, as it involves multiple-translations units (and there is still only one definition in any one of the translation units).
- Every program shall contain exactly one definition of every non-inline function or variable that is odr-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 odr-used.
This rule covers the program level (may contain several translation units), but it only affects non-inline function or viable that are odr-used. Class type definition included in different translation units is not covered and does not violate this rule.
And, there is a rule that specifically deals with definitions of a class type appearing in different translation units:
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 short, you can define the class type multiple times in different translation units, but each translation unit can have at most one definition, and all the definitions must be the same.