-1

An "ordinary" function, when defined and used exclusively in a single translation unit is declared and defined like this:

// implementation.cpp
static void fun(int arg) { /* implementation */ }

alternatively you could drop the static keyword and wrap the function in an unnamed namespace. But failing to do one of the above can lead to ODR violations: if a different translation unit also has a declaratino/definition of fun (with the same arguments) the liker may silently have to discard one of the implementations to keep a single definition (in which case you'd better have the exact same definition spelled out for both, or you'll see unexpected behaviour).

As described above, the problem is more "implementation oriented", but even the standard would urge you to protect against such side effects using static or namespace { /**/ }.

The question is what happens when fun is a function template? Should I also require wrapping it in an unnamed namespace or declaring it as static inside a translation unit?

template <class T>
/* static ? */ void fun(T arg) { /*...*/ }

In cppreference it is mentioned that if the function is inline it's ok obviously (requirements for single definition are only posed for non-inline functions) so no ODR worries there:

One and only one definition of every non-inline function or variable that is odr-used (see below) is required to appear in the entire program

We know that function templates are inlined but is this enough to guarantee ODR correctness?

Lorah Attkins
  • 5,331
  • 3
  • 29
  • 63
  • Your quote is the One Definition Rule for non-inline functions and variables. Read on to the paragraph after that for the requirements for inline functions. (No, the definition does **not** melt away. The `inline` keyword has nothing to do with using the function contents directly, a.k.a. inlining the function.) – JaMiT Jan 07 '21 at 15:28
  • @JaMiT So I **should** use static or put it in an unnamed namespace? (I'll edit the text before the quotation to better connect the dots between inline and non) – Lorah Attkins Jan 07 '21 at 15:40
  • You can have as many template instances as you want. The linker will take one and everything is ok. It is the normal use case that we instantiate multiple instances in different compilation units. Nothing special to do for such case. – Klaus Jan 07 '21 at 15:42
  • @Klaus Obviously the problem will rise when another author will try to declare `fun` as something else in a different translation unit. That's what happens in the non template case as well (if we have a simple case with a single `main.cpp` not many people use unnamed namespaces). I'm not asking about multiple instantiations, but what is the best practice, should I proactively declare it as static? If a different translation unit contains `template fun(T arg) { /* different implementation here*/ } will there be a(n ODR) problem? – Lorah Attkins Jan 07 '21 at 15:49
  • OK, you mean to say: "Someone else" will create a template function with same signature in a different file and use it from different compilation units. OK. But if you fear about such things, you also should fear that someone else creates also the namespace with different content and add other stuff which violates ODR. Sorry, but this looks not like a technical problem but something around project management, reviews and documentation. If such can happen, how you define include orders for example? Only my two cents – Klaus Jan 07 '21 at 16:21
  • @Klaus What you say can't happen. Unnamed namespaces are designed to have no name hence you can't reproduce the same name in a different translation unit. The way they work is by being assigned a program wide unique name inside the translation unit (e.g. __unnamed_nmspc_5255111_cxx) which is then injected in that tu (using __unnamed_nmspc_5255111_cxx). The very reason why we use these techniques is to avoid what you describe. `static` also works by containing that definition inside the tu, I won't described possible implementations cause it seems we're already over complicated this. – Lorah Attkins Jan 07 '21 at 16:52
  • What I want to say: If "someone" can add "something" per accident which perfectly fits in signature to all your interfaces, compiles and links but does something totally different AND in parallel you have these kind of additions in your code base in parallel and you are able to manage that some headers are included at one and others on a another place... sorry. It sounds like an total obscure setup of the project. – Klaus Jan 07 '21 at 17:17

1 Answers1

1

In cppreference it is mentioned that if a function (with external linkage) is marked inline then extra care must be taken because the definition must be present in every translation unit that uses that function. When there is more than one definition, several conditions must be met including:

each definition consists of the same sequence of tokens

If this requirement is not satisfied, the program is ill-formed, no diagnostic required. This requirement applies equally to inline functions and function templates. Sometimes this will work out ok, but in cases where the compiler chooses to not inline a call to your function (the inline keyword does not influence this), you could end up calling the wrong version of your function. Linkers are not required to detect this.

In fact, this is the ODR violation that linkers tend to silently ignore. Despite what is claimed in the question, when there is more than one definition of a non-inline function with external linkage, linkers tend to produce errors and abort.

Note: Marking a function static or enclosing it in an anonymous namespace will cause it to no longer have external linkage.


I would like to note that this question uses the word "obviously" in a fairly common, but non-standard, way meaning "I need the following false statement to be true, so accept it without question." It can be useful to learn to stop and quesion yourself whenever you feel the need to justify something as "obvious".

JaMiT
  • 14,422
  • 4
  • 15
  • 31
  • The question has nothing to do with the the definition not being present and everything to do with the definition being visible/available more than one times. I'm not worried that my translation unit won't be able to find the function declared inside it; I'm worried that future development may break my program because a different author decides to declare a different function template that happens to have the same name and none of us bothered to use `static` or an `unnamed namespace`. – Lorah Attkins Jan 07 '21 at 16:06
  • *"I'm not worried that my translation unit won't be able to find the function declared inside it"* -- you should be. If a different author decides to declare a different function template that happens to have the same name, your translation unit may end up calling the other author's template. – JaMiT Jan 07 '21 at 16:09
  • cppreference says that having a function `fun` declared and defined inside `source1.cpp` and `source2.cpp` (with different implementation) is not an ODR violation when `fun` is inline and poses a restriction of "exactly one" definition ONLY on non-inline functions. Why? My gues is because when a function is inline you cannot find its symbol in the object file since its contents are used directly, so the linker has no fear of discarding one of the definitions – Lorah Attkins Jan 07 '21 at 16:10
  • @LorahAttkins Double check the page you are reading. That might be the rules for C rather than the rules for C++. – JaMiT Jan 07 '21 at 16:12
  • I appreciate your effort looking into this, but I'm asking a completely different thing. Is something which is common practice for ordinary functions (declaring them as static in cpp files) needed for function templates or not? that's all ... – Lorah Attkins Jan 07 '21 at 16:13
  • @LorahAttkins Being a template is completely irrelevant. – JaMiT Jan 07 '21 at 16:17
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/226986/discussion-between-jamit-and-lorah-attkins). – JaMiT Jan 07 '21 at 16:18