Alternative C/C++ type definitions

I was messing around and expecting the following to blow up when I tried to compile it but to my shock it totally works. Can someone please explain to me how/why the following works.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// foo.h
#ifndef FOO_H
#define FOO_H

#ifdef __cplusplus
#include <iostream>
extern "C" {
#endif

typedef struct foo {
    float x;
    float y;
    float z;

#ifdef __cplusplus
    float w;

    inline foo operator+(const foo& addend) {
        foo result = {
            x + addend.x,
            y + addend.y,
            z + addend.z,
            w + addend.w
        };
        return result;
    }
#endif

}foo;

#ifdef __cplusplus

} // End C linkage

inline std::ostream& operator<<(std::ostream &os, const foo &inst) {
    os << inst.x << ' ' << inst.y << ' ' << inst.z << ' ' << inst.w << '\n';
    return os;
}

#endif

#endif // FOO_H

//cfoo.c
#include "foo.h"
extern foo c_foo;
foo c_foo = { 3.0f, 3.0f, 3.0f };

// main.cpp
#include "foo.h"
#include <iostream>

extern "C" foo c_foo;
static foo cpp_foo = { 1.0f, 2.0f, 3.0f, 4.0f };
int main(){
    foo a = cpp_foo + c_foo;
    std::cout << c_foo;
    std::cout << cpp_foo;
    std::cout << a;

    float w = a.w;
    std::cout << w << std::endl;

    std::getchar();
    return 0;
}


This certainly seems like a disaster waiting to happen, especially since sizeof() gives a different value depending on if it is used in a .c or .cpp file. I was expecting a multiple definition error but MSVC, Clang, and G++ all compiled and ran the program just fine. I would like to understand why.

Edited by Jason Smith on Reason: Initial post
ODR rule. If you violate it, you get undefined behavior.

From C++ standard (3.2 One Definition Rule):

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

[...]

There can be more than one definition of a class type [...] 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

[...]

If the definitions of D satisfy all these requirements, then the program shall behave as if there were a single definition of D. If the definitions of D do not satisfy these requirements, then the behavior is undefined.


Your definition of foo struct has different sequence of tokens in different translation units. Thus undefined behavior.

Edited by Mārtiņš Možeiko on
I also noticed the following on cppreference:

the language linkage is the same (e.g. the include file isn't inside an extern "C" block)


I'm thinking that this also contributes to this being undefined?