7

So we put class definitions in headers and we do not include translation units rather than headers because of the ODR (there probably are more reasons). Also, because of the ODR, you should not add definitions of variables and functions etc. in headers because it is likely to break the rule....

But since ODR also applies for class types (which to me is class definitions), why can that be added in headers without caution?

  • Because the standard says so. You can also put inline functions into a header. –  Aug 26 '17 at 22:11
  • @manni66 I know that it is possible. Btw, the ODR does not apply for inline functions anyway, so of course you can do that.. –  Aug 26 '17 at 22:12

2 Answers2

7

There are several statements in the "3.2 Ondefinition rule" section of the standard, that covers different aspects of the rule.

  1. 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).

  1. 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:

  1. 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.

fefe
  • 3,342
  • 2
  • 23
  • 45
  • So a class definition is not odr-used and that is why you would include that in a header? Including cpp files would very likely break the ODR rule, right? Not because they are odr-used but because there are multiple definitions of the same things... Isn't that the same for headers and class definitions? I know that I am maybe just repeating myself.. –  Aug 27 '17 at 11:29
  • I mean, both when including headers that contain class definitions and translation units that contain all the other definitions, you get a chance of having multiple definitions and breaking the ORR-rule, so isn't the choice between including cpp files or h files based on this? Like in this answer: https://softwareengineering.stackexchange.com/questions/254233/why-do-we-need-to-include-the-h-while-everything-works-when-including-only-the/254250#254250 –  Aug 27 '17 at 11:29
  • @FacPam Not every entity in the program follows the same ODR rule. They follow different rules. You can have only one class definition (of the same class) in a translation unit, but you can have multiple class definitions in the same program (as long as they are the same). But you can only have one definition of non-inline functions or variables across the whole program. Class definition and variable definitions follow different ODR rules. – fefe Aug 28 '17 at 01:32
  • And for clarification: If a member function is defined in the class, they are inline by default and do not follow the ODR-rule for non-inline functions. The ODR rule for an inline function is also included in clause 5 (quoted in the answer), which is the same as the ODR-rule for the class definition. – fefe Aug 28 '17 at 02:05
  • Ohhhhhhhh I get it now! Thank you so much, I do not know why you want to spend your time helping me, but thanks man! You have really made my day :) –  Aug 28 '17 at 06:33
4

You are confusing between ODR and ODR-use.

ODR of a type means that a class cannot defined more than once in a translation unit.

If you have the following in a translation unit, the compiler should report an error since the ODR of type foo is violated.

struct foo {};
struct foo {};

void bar() {}

ODR-use does not apply to types. It applies to non-inline functions and variables.

Both ODR and ODR-use are described in the link from your post.

An SO post on ODR-use: What does it mean to "ODR-use" something?.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 2
    I'd never heard the term ODR-use(d), before and I suspect others may not - this answer https://stackoverflow.com/questions/19630570/what-does-it-mean-to-odr-use-something explains it. –  Aug 26 '17 at 22:17
  • I think that I already know this... but maybe I did not make myself clear when asking the question... You would not include cpp files because they usually contain definitions of functions etc. and you cannot have two definitions in one translation unit which would be likely to happen when including cpp files; why then include headers that contain class definitions when you cannot have two class definitions in one translation unit either? Isn't those two cases the same? –  Aug 26 '17 at 22:26
  • I mean isn't there there the same chance of breaking the odr when including class definitions(h files) as when including function definitions(cpp files)? Or did I misunderstand something? –  Aug 26 '17 at 22:28
  • @FacPam, no they are not the same. A class definition is sort of like a template to create objects. The template must, of necessity, be available to any translation unit that wants to construct an object of the type or a call a member function of the type. A class definition does not have any executable code. Only member functions of the class do. – R Sahu Aug 26 '17 at 22:30
  • @FacPam, there is a chance of that happening. In fact, the standard has specific guidelines on class definitions across translation units. – R Sahu Aug 26 '17 at 22:31
  • Well, thanks. But you cannot have two definitions of the same class in one translation unit as you cannot have two definitions of the same function in one translation unit, so I still do not see the different for why you couldn't just add function definition(non-inline) in headers as well or include cpp files.. Then, is it not a question about trying to not break the ODR that you are including headers rather than cpp files? I mean, isn't this why you have declarative code in headers :/ –  Aug 26 '17 at 22:34
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/152919/discussion-between-r-sahu-and-fac-pam). – R Sahu Aug 26 '17 at 22:38
  • @RSahu I am new to this chat thing and do not know if you get notifications so just in case you have not - I would let you know that I have left some questions :) –  Aug 27 '17 at 11:25