Why One Definition Rule, not One Declaration Rule?
Asked Answered
S

5

6

I have read materials below:

https://www.wikiwand.com/en/One_Definition_Rule

http://en.cppreference.com/w/cpp/language/definition

What is the difference between a definition and a declaration?

But still, can't figure out why it is One Definition Rule rather than One Declaration Rule?

I maintain that declaration is a subset of definition, so One Definition Rule is enough.

Stercoraceous answered 28/10, 2017 at 9:15 Comment(1)
Separate compilation units. Each compilation unit must produce the same definition.Casarez
I
4

Definition is a subset of declaration, not the other way around. Every definition is a declaration, and there are declarations that are not definitions.

int i = 3;      // definition and declaration
extern int i;   // ok: (re)declaration
int i = 4;      // error: redefinition

extern int j;   // declaration
extern int j;   // ok: (re)declaration
int j = 5;      // ok: (re)declaration and definition
int j = 6;      // error: redefinition
Inimical answered 28/10, 2017 at 12:42 Comment(0)
H
7

One declaration rule would be too strict, preventing programs that use the same header more than once from compiling. It would also make it impossible to define data structures with back references.

A simple way to see the first point (using headers) is to consider a program composed of two translation units, A.cpp and B.cpp, which both include <string> header.

Header included twice

Translation units A.cpp and B.cpp are translated independently. By including <string>, both translation units acquire a declaration of std::string.

As for the second point (data structures with back references) consider an example of defining a tree in which each node has a back reference to its parent tree:

// Does not compile
struct tree {
    struct node *root;
};
struct node {
    struct node *left;
    struct node *right;
    struct tree *owner;
};

This example would not compile, because node from struct node *tree is undeclared. Switching the order of struct node and struct tree declarations wouldn't help, because then tree from struct tree *owner would be undeclared. The only solution in C and C++ is to introduce a second declaration for either of the two structs.

Hexa answered 28/10, 2017 at 9:27 Comment(1)
Include guards already ensure <string> is only included once. (Also, why <string.h> and std::string? ODR is for multiple translation units.Assail
E
6

Because the same declaration, in a .h file, may be included in multiple compilation units, and because multiple definitions is definitely a programming error, whereas multiple declarations isn't.

Equipage answered 28/10, 2017 at 9:23 Comment(3)
multiple definitions is definitely a programming error, whereas multiple declarations isn't, can you explain why, as in this form it seems only a reformulation of OP's question.Conners
@Conners 'Because the same declaration, in a .h file, may be included in multiple compilation units.'Equipage
@Conners More simplify: int f(int); int f(int); intf(int a) { return a++;} //multiple declarations in source is allowed. So, mumltiple declarations is validStercoraceous
I
4

Definition is a subset of declaration, not the other way around. Every definition is a declaration, and there are declarations that are not definitions.

int i = 3;      // definition and declaration
extern int i;   // ok: (re)declaration
int i = 4;      // error: redefinition

extern int j;   // declaration
extern int j;   // ok: (re)declaration
int j = 5;      // ok: (re)declaration and definition
int j = 6;      // error: redefinition
Inimical answered 28/10, 2017 at 12:42 Comment(0)
T
2

Because when you have declared a function in a header file

// header toto.h
int f(void);

and you want to define it in the compilation unit where it belongs, you'd do

#include "toto.h"

int f(void) {
   return 0;
}

The definition is also a declaration, so this compilation unit sees two declarations, one in the header and one in the .c or .cpp file.

In short, the multiple declaration rule allows to check for consistency between different source files.

Tenorite answered 28/10, 2017 at 9:35 Comment(0)
Q
1

The reason is really that the C++ translation model can easily deal with conflicting multiple declarations; it just requires the compiler part of the toolset to detect errors like this in the source code:

int X();
void X(); // error

A compiler can easily do that.

And when there are no such errors in any translation units, then there's no problem; every X() call in every translation unit is identical; what remains to do is for the linker to link every call to the one correct destination. The declarations have done their job and no longer play a role.


Now with multiple definitions, it's not that easy. Definitions are something which concerns multiple translation units and which goes beyond the scope of the compilation phase.

We've already seen that in the example above. The X() calls are in place, but now we need the guarantee that they all end up at the same destination, the same definition of X().

That there can be only one such definition should be clear, but how to enforce it? Put in simple terms, when it's time to link the object code together, the source code has already been dealt with.

The answer is that C++ basically chooses to put the burden on the programmer. Forcing compiler/linker implementors to check all multiple definitions for equality and detect differences would be beyond the capabilities of C++ toolsets in most real-life situations or completely break the way those tools work, so the pragmatic solution is to just forbid it and/or force the programmer to make sure that they are all identical or else get undefined behaviour.

Querulous answered 28/10, 2017 at 12:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.