2

A bit of a weird one and not very practical, but I'm trying some random things with inline in cpp and I thought about trying this:

 inline void foo(void){static int x=0; x++; cout << x << '\n'; return;};

and in another .cpp file I've got:

 inline void foo(void){static int x=0; x=x+2; cout << x << '\n'; return;};

Now, this works for some reason( same function type/name) but the different body, they both share the same 'x' but their definition is not the same. I would expect the compiler to complain, but it's not. why is that?

Jason
  • 36,170
  • 5
  • 26
  • 60
Pol
  • 185
  • 9

2 Answers2

3

The program is ill-formed no diagnostic required as per basic.def.odr#13:

  1. There can be more than one definition of a
  • inline function or variable ([dcl.inline]),

in a program provided that each definition appears in a different translation unit and the definitions satisfy the following requirements. Given such an entity D defined in more than one translation unit, for all definitions of D, or, if D is an unnamed enumeration, for all definitions of D that are reachable at any given program point, the following requirements shall be satisfied.

  • Each such definition shall consist of the same sequence of tokens, where the definition of a closure type is considered to consist of the sequence of tokens of the corresponding lambda-expression.
  1. If these definitions do not satisfy these requirements, then the program is ill-formed; a diagnostic is required only if the entity is attached to a named module and a prior definition is reachable at the point where a later definition occurs.

(emphasis mine)

And since the function definitions in your example does not consist of the same sequence of tokens, the program is ill-formed no diagnostic required.

Jason
  • 36,170
  • 5
  • 26
  • 60
  • If the two functions are each in anonymous namespaces, they should have internal linkage and hence no ODR violation, right? We're just assuming both functions have external linkage because the question doesn't explicitly show otherwise. – Useless Dec 13 '22 at 13:20
  • @Useless Yeah, I assumed(since OP didn't write explicitly anything about namespace) that both of the above functions are defined in global namespace. – Jason Dec 13 '22 at 13:23
  • @Useless I was wondering about that, does that mean we should define functions in header files as `static` not `inline`? What problems if any would there be with that? – john Dec 13 '22 at 14:45
  • @john AFAIK `static` will affect the linkage(like making the function to have internal linkage) but `inline` is different. It depends on what program you have but I would prefer `inline` over `static` for functions in header. – Jason Dec 14 '22 at 06:48
2

You are falling in an undefined behaviour.

The compiler does not say nothing because:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement; 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 [class.ctor], [class.dtor] and [class.copy]). An inline function or variable shall be defined in every translation unit in which it is odr-used outside of a discarded statement.

more details about that here and in the cppreference site

Zig Razor
  • 3,381
  • 2
  • 15
  • 35
  • Would it still count as undefined behaviour if the definition was the exact same? I am guessing not since it would have the same effect the same as including the definition in the header in the first place, just making sure. Additionally, what would happen if I were to declare the function inline in a header and omit it from its definition in a .cpp file, or vice versa? Thank you! – Pol Oct 26 '20 at 13:16
  • is ever an Undefined Behaviour, because you don't know which definition are you taking. In the case the definition are exactly the same obviously the result are equal, but this doesn't mean that isn't UB. – Zig Razor Oct 26 '20 at 13:18
  • Could you elaborate more on this, since I am a bit confused? Wouldn't having the exact same definition in two places have the same effect as including it in a header file and then including this in each TU? So why could it still be UB? – Pol Oct 26 '20 at 13:26
  • is UB because you have in any case multiple definition of the same function. Also with the same behaviour, the include file will be different and not ever the inline function are trasposed by the compiler in the code where they are called. Sometimes the compiler do some optimization so nothing is granted – Zig Razor Oct 26 '20 at 13:29
  • 1
    The comments are wrong. It's *not* UB if the definitions are identical. (Of course, not just syntactically identical, but where all the tokens have the same meaning in every definition). – cigien Oct 26 '20 at 14:00
  • Thank you @cigien, I was confused about this. So assuming they are syntactically identical, what do you mean about the tokens being the same? Wouldn't this be guaranteed if they were identically syntaxed? – Pol Oct 26 '20 at 14:23
  • 1
    @pol Well, technically no. e.g. you might have a macro that `#define`s a token to mean something else in one definition than another. That would be UB. There are other ways to change the meaning, but the rule is basically, "all tokens must look *and* mean the same thing in every definition". – cigien Oct 26 '20 at 14:28
  • I see. But therefore in most cases, identical syntax would mean it should be fine from what I am getting then. Thank you for your help and clarification! – Pol Oct 26 '20 at 14:44
  • @cigien sorry i have misunderstud the question, obviously if the file are identical can'tbe UB. Thank you for your comment – Zig Razor Oct 26 '20 at 14:56
  • @cigien I've added an answer that specifies that the code is IFNDR and exactly why. – Jason Dec 13 '22 at 13:04
  • @ZigRazor In the standard they manage an example (http://eel.is/c++draft/basic.def.odr#16) where an identical copy actually falls foul of this: `inline void g(bool cond, void (*p)() = []{}) {...}`. Because the default argument lambda results in distinct closure types in different translation units this is UB. Blew my mind a bit when I saw it. :) – Frodyne Dec 13 '22 at 13:12