Does inline determine internal linkage?
Asked Answered
K

1

6

I'm trying to extern an inline function. How I thought it should work:

//a.cpp
inline void f(int) {}
//b.cpp
extern void f(int);
int main() { f(4); }

But getting link error. Then by reading this ("1) It must be declared inline in every translation unit."). What I've tried:

//a.cpp
inline void f(int) {}
//b.cpp
extern inline void f(int);
int main() { f(4); }

Still getting link error. But now, trying something that I don't know what I'm doing:

//a.cpp
extern inline void f(int) {}
//b.cpp
extern inline void f(int);
int main() { f(4); }

It works. What's happening here? Before adding extern to everything, f in a.cpp had internal linkage?

I'm using MSVC 2017 (v141) with /permissive- and /std:c++17

Kalvin answered 28/5, 2019 at 20:5 Comment(12)
It would help to mention the compiler you are using (tag it)Infertile
extern inline void f(int) {} looks a bit strange. You've given a definition for a function that is marked as being defined elsewhere.Laudian
The definition of an inline function or variable (since C++17) must be present in the translation unit where it is accessed. The function inline void f(int) {} already has external linkage and must be defined in b.cpp. The function already has external linkage. static inline void f(int){} has internal.Clough
FWIW, you can just define the inline function in a header file that is included by all the files that need it.Argueta
@Clough It is both a declaration and a definition though.Redvers
@Clough To expand on previous: All definitions are declarations.Pincushion
extern inline void f(int); will only allow you to take an address of f but not invoke f() directly. You still need to provide the definition in each translation unit for that.Salable
I think inline and extern are mutually exclusive. You're telling the compiler to put the function in the translation unit (inline) and telling him to go and look for it in another translation unit (extern)Cruller
Think that an inline function could be defined in more than one translation unit (that's what it means). Which one is extern going to choose?Cruller
Anyway, what are you trying to accomplish by using extern and inline at the same time?Cruller
I'm trying anything, just curious about how it works. And I agree that it makes no sense.Mcvey
You might be interested in my answer here (along with others on that question) but note that I only know GCC-specific information, not MSVC-specific information (if anyone can add it, that would be great).Lorant
A
7

I'm trying to extern an inline function.

There is no reason to use extern with a function. See storage duration - linkage. Functions have external linkage by default; in order to not have external linkage, something special needs to be done (i.e. put it in an anonymous namespace or declare it static). So the normal use of an inline function already exhibits external linkage with no need for the extern keyword.

How I thought it should work:

//a.cpp
inline void f(int) {}
//b.cpp
extern void f(int);
int main() { f(4); }

Then by reading this ("1) It must be declared inline in every translation unit.").

That reference is correct, but look up a bit more where it says "The definition of an inline function [...] must be present in the translation unit where it is accessed [...]." Your example has a declaration of f in b.cpp, but not a definition. If you are going to call f from b.cpp, you need the full definition in that translation unit, as in:

inline void f(int) {}

(This is the same code that exists in a.cpp.) If you leave off the curly braces, then you have a declaration but not a definition, making it illegal to call f from that translation unit.

Basically, it is a real pain to define an inline function outside a header file, unless you give it internal linkage. That's because each source file that uses the inline function would need its own copy of the function body, meaning that if you change the function, you need to make that change in multiple files. Oof. Don't do it. Define each of your inline functions in a header file. If you think you want to define one in a source file, you probably misunderstand what "inline" means.


What does "inline" mean?

As far as the compiler is concerned, the inline keyword means (almost) nothing. It is just a flag on a function definition that gets propagated into the object code so that the linker sees it. The compiler processes the function just as it would any other function. The function may be called normally, or calls to it might be inlined – just like any other function.

The one case where the compiler might do something with the inline flag is when a function is declared inline, is used, but lacks a definition. This is an error that can be caught before the linker takes over. It does not have to be caught by the compiler, but it can be. (If not caught by the compiler, it would be caught by the linker.)

Moving on to the linking stage. When the linker sees the inline flag, it suspends the one-definition rule for that function. The linker will expect to see a definition of the function in each translation unit that still uses the function after the compiler's optimizations. It gets to choose any one of those definitions to serve as the final implementation. Hence the reason that all definitions must match.

And that's about it. The inline keyword basically means that the function definition is in a header file. It tells the linker to not complain when that definition appears in multiple translation units, because that is expected.

Going back to the question, it looks like the intent was to declare an inline function whose definition would appear in only one translation unit. In other words, the function would be flagged as defined in multiple translation units, but the definition would be in only one. Kind of inconsistent there, if not outright contradictory.

Affirmative answered 28/5, 2019 at 21:57 Comment(1)
"The function may be called normally, or calls to it might be inlined – just like any other function.". This is not entirely true. For Clang at least, the inline specifier -- when applied to a function -- adjusts the inlining cost threshold and makes it more likely that the function gets inlined.Connate

© 2022 - 2024 — McMap. All rights reserved.