Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Inline functions and locals

123 views
Skip to first unread message

Mut...@dastardlyhq.com

unread,
Jan 12, 2022, 11:34:32 AM1/12/22
to
I'm curious as to why returning a reference to a local inside an inline
function isn't (apparently) allowed. eg:

#include <iostream>
#include <string>

using namespace std;

inline string &func()
{
string s = "hello";
return s;
}



int main()
{
cout << func() << endl;
return 0;
}

This causes a compilation warning with clang and when run prints out garbage
as you'd expect if it were a non inline. However surely if the function is
truly inline it should work. Are locals for inlines temporaries created on the
heap instead of being stored on the same stack as the calling function locals?

Bonita Montero

unread,
Jan 12, 2022, 11:39:00 AM1/12/22
to
Yes, this is UB because the storage of the function and sometimes
the object you return get destructed. Make it thread_local and then
you can return the object from the function and the function can be
called from any thread. Make it static and lock any operations on
it if you want to have a shared object.

James Kuyper

unread,
Jan 12, 2022, 12:48:13 PM1/12/22
to
Calls to functions declared "inline" have the same semantics as they
would have if not so declared, they can simply be optimized by replacing
the function call with local code, but only if that local code has the
SAME semantics. The following code is equivalent to what the compile is
allowed to do with inline functions. To make this work, I need to
explicitly declare a variable that corresponds to the return value from
the function, but I'm going to have to change it to a pointer rather
than a reference, because this re-write wouldn't work with a reference.
For a great many purposes, references are semantically equivalent to
const pointers, the differences are mainly syntactic. However, for
reasons that will hopefully be clear when you think about it, I can't
use a const pointer OR a real reference in the following re-write:

int main()
{
string *tmp;

{
string s = "hello";
tmp = &s;
}

cout << *tmp << endl;
return 0;
}

The object `s` in func() and the object `s` in my re-write both have
lifetimes that end when the `}` that terminates the enclosing block is
reached. Dereferencing tmp at any time after the lifetime of the object
it refers to has ended has undefined behavior, for the same reason that
using the reference returned by func() has undefined behavior. So both
pieces of code are bad for the same reason.

I suspect that you thought that the inlined function code would not be
treated as if it were enclosed in a separate block, but that would
change the semantics. Objects declared local to the inlined function are
NOT treated as if they were declared in the block that contains the call
to that function - if they were, that would make them much harder to use.

Juha Nieminen

unread,
Jan 13, 2022, 2:00:22 AM1/13/22
to
Mut...@dastardlyhq.com wrote:
> I'm curious as to why returning a reference to a local inside an inline
> function isn't (apparently) allowed. eg:

For all intents and purposes 'inline' (when used in a function definition)
is an instruction for the linker, not the compiler, and should really be
thought as such.

In all likelihood the compiler is going to ignore the keyword when making
inlinining decisions (I don't know if it has ever had any effect with any
compiler). In any case, it doesn't change the semantics of the function.
The function still has all the requirements of a "non-inline" function
(with the exception of the changed linking strategy). You should always
think of "inline" functions as if they weren't inlined (which is a real
possibility, because the standard allows compilers to not actually do the
inlining.)

'inline' is an instruction for the linker because it instructs the linker
to do something special with that function signature. (It tells it that
if it finds that function implementation in more than one compilation
unit, rather than give an error for duplicate symbols it should just
choose one of them and discard the rest.)

Alf P. Steinbach

unread,
Jan 13, 2022, 4:46:15 AM1/13/22
to
On 13 Jan 2022 08:00, Juha Nieminen wrote:
> Mut...@dastardlyhq.com wrote:
>> I'm curious as to why returning a reference to a local inside an inline
>> function isn't (apparently) allowed. eg:
>
> For all intents and purposes 'inline' (when used in a function definition)
> is an instruction for the linker, not the compiler, and should really be
> thought as such.

I guess here you're referring to the only guaranteed feature of
`inline`, that of allowing multiple definitions of variable or function,
as long as they're in different translation units.


> In all likelihood the compiler is going to ignore the keyword when making
> inlinining decisions (I don't know if it has ever had any effect with any
> compiler).

Some years ago g++ treated `inline` as an almost absolute inlining
directive.

That was problematic wrt. header only modules.

I guess it still is, but negative facts about g++ are hard to become
aware of: the gcc fanboi community tends to assemble like a swarm of
mosquitoes (including even in this group, which was surprising to me the
first few times, it was easy to understand for SO but unbelievable here)
when there's mention of something not 100% hallelujah about gcc.


> In any case, it doesn't change the semantics of the function.
> The function still has all the requirements of a "non-inline" function
> (with the exception of the changed linking strategy). You should always
> think of "inline" functions as if they weren't inlined (which is a real
> possibility, because the standard allows compilers to not actually do the
> inlining.)
>
> 'inline' is an instruction for the linker because it instructs the linker
> to do something special with that function signature. (It tells it that
> if it finds that function implementation in more than one compilation
> unit, rather than give an error for duplicate symbols it should just
> choose one of them and discard the rest.)

- Alf

Bonita Montero

unread,
Jan 13, 2022, 5:08:26 AM1/13/22
to
Am 13.01.2022 um 08:00 schrieb Juha Nieminen:

> 'inline' is an instruction for the linker because it instructs the linker
> to do something special with that function signature. ...

If you don't use link-time compiling the linker doesn't even see the
inline-function.

Paavo Helde

unread,
Jan 13, 2022, 5:46:57 AM1/13/22
to
Declaring a function inline does not change the semantics of the
function. In particular, it must not change the lifetime of objects.
Imagine a RAII mutex lock like std::lock_guard, extending its lifetime
without programmer's consent might be disastrous.

So even if the function really gets inlined, the compiler must ensure
the locals are destroyed at exactly the same moment as without inline.



Mut...@dastardlyhq.com

unread,
Jan 13, 2022, 10:39:02 AM1/13/22
to
On Thu, 13 Jan 2022 12:46:39 +0200
Paavo Helde <ees...@osa.pri.ee> wrote:
>12.01.2022 18:34 Mut...@dastardlyhq.com kirjutas:
>> I'm curious as to why returning a reference to a local inside an inline
>> function isn't (apparently) allowed. eg:
>>
>> #include <iostream>
>> #include <string>
>>
>> using namespace std;
>>
>> inline string &func()
>> {
>> string s = "hello";
>> return s;
>> }
>>
>>
>>
>> int main()
>> {
>> cout << func() << endl;
>> return 0;
>> }
>>
>> This causes a compilation warning with clang and when run prints out garbage
>> as you'd expect if it were a non inline. However surely if the function is
>> truly inline it should work. Are locals for inlines temporaries created on
>the
>> heap instead of being stored on the same stack as the calling function
>locals?
>
>Declaring a function inline does not change the semantics of the
>function. In particular, it must not change the lifetime of objects.
>Imagine a RAII mutex lock like std::lock_guard, extending its lifetime
>without programmer's consent might be disastrous.

Good point.


Andrey Tarasevich

unread,
Jan 13, 2022, 8:52:06 PM1/13/22
to
On 1/12/2022 8:34 AM, Mut...@dastardlyhq.com wrote:
> I'm curious as to why returning a reference to a local inside an inline
> function isn't (apparently) allowed. eg:
>
> #include <iostream>
> #include <string>
>
> using namespace std;
>
> inline string &func()
> {
> string s = "hello";
> return s;
> }
>
>
>
> int main()
> {
> cout << func() << endl;
> return 0;
> }
>
> This causes a compilation warning with clang and when run prints out garbage
> as you'd expect if it were a non inline. However surely if the function is
> truly inline it should work.

Um... You are seriously confused. This is a rather widespread
misconception that keyword `inline` has something to do with "inlining"
function code at the call site.

1. "Declaring function as inline" and "embedding (inlining) function
code at a specific call site" are two completely different, independent,
unrelated things. In your example you simply declared a function as
`inline`, yet in your post you seem to be talking about embedding at a
specific call site. This makes no sense at all.

2. Keyword `inline` has absolutely nothing to do with embedding function
code at the call site. The purpose of `inline` keyword is to allow you
to defeat ODR restrictions for a function (or a variable) with external
linkage. By using `inline` you can provide multiple definitions of
functions (or variables) in your program and still not get those
"multiple definitions" complaints from the linker. That's the only thing
keyword `inline` does. That's what it is for.

3. Compiler decisions about embedding function code at a specific call
site is made independently at each call site. These decisions are not in
any way related to keyword `inline`. Even when a specific calls is
embedded, it still preserves the "normal" unembedded semantics of the
call. Local variables are destroyed at the same spot as they would be
destroyed in a normal call. You can't return references to automatic
variables from functions, period.

So, again, you seem to be mixing to completely unrelated matters. Don't
do this.

> Are locals for inlines temporaries created on the
> heap instead of being stored on the same stack as the calling function locals?

Strange question. Yes, they _are_ stored on the same stack. But why does
it matter? The language says that automatic variables are gone once the
function finishes working. That's all you need to know. Especially when
it comes to such non-trivial objects as `std::string`, whose lifetime is
not really limited by storage duration, but rather by
construction/destruction timeline.

--
Best regards,
Andrey Tarasevich

Andrey Tarasevich

unread,
Jan 13, 2022, 10:30:22 PM1/13/22
to
Not true.

Whether the linker sees or doesn't see inline functions is unpredictable
(and therefore irrelevant). And inline function still has external
linkage, unless explicit steps are taken to give it internal linkage.

If the compiler decides to generate a regular body for an inline
function (e.g. because it decided to implement some call sites in this
TU as regular calls, because address of the function is taken, or simply
because it felt like it), the linker _will_ definitely see the generated
function body, just like for any other functions.

And this is actually the purpose of `inline` keyword: tell the linker
that if it encounters _multiple_ regular bodies of the same inline
function, it has to discard all but one of them (instead of complaining).

Mut...@dastardlyhq.com

unread,
Jan 14, 2022, 6:22:31 AM1/14/22
to
On Thu, 13 Jan 2022 17:51:45 -0800
Andrey Tarasevich <andreyta...@hotmail.com> wrote:
>On 1/12/2022 8:34 AM, Mut...@dastardlyhq.com wrote:
>> I'm curious as to why returning a reference to a local inside an inline
>> function isn't (apparently) allowed. eg:
>>
>> #include <iostream>
>> #include <string>
>>
>> using namespace std;
>>
>> inline string &func()
>> {
>> string s = "hello";
>> return s;
>> }
>>
>>
>>
>> int main()
>> {
>> cout << func() << endl;
>> return 0;
>> }
>>
>> This causes a compilation warning with clang and when run prints out garbage
>> as you'd expect if it were a non inline. However surely if the function is
>> truly inline it should work.
>
>Um... You are seriously confused. This is a rather widespread
>misconception that keyword `inline` has something to do with "inlining"
>function code at the call site.
>
>1. "Declaring function as inline" and "embedding (inlining) function
>code at a specific call site" are two completely different, independent,
>unrelated things. In your example you simply declared a function as

Wikipedia says otherwise:

https://en.wikipedia.org/wiki/Inline_function

"It serves as a compiler directive that suggests (but does not require) that
the compiler substitute the body of the function inline by performing inline
expansion, i.e. by inserting the function code at the address of each function
call,"

Perhaps you're the one who's getting confused?

Öö Tiib

unread,
Jan 14, 2022, 7:02:31 AM1/14/22
to
Wikipedia is (in kind of populist/politician manner) saying very same thing
that Andrey said. Read the Wikipedia article carefully (and as whole).
Does the sentence you quoted say that functions that you have declared
inline will be inlined, ever?

James Kuyper

unread,
Jan 14, 2022, 8:35:10 AM1/14/22
to
On 1/14/22 6:22 AM, Mut...@dastardlyhq.com wrote:
> On Thu, 13 Jan 2022 17:51:45 -0800
> Andrey Tarasevich <andreyta...@hotmail.com> wrote:
...
>> Um... You are seriously confused. This is a rather widespread
>> misconception that keyword `inline` has something to do with "inlining"
>> function code at the call site.
>>
>> 1. "Declaring function as inline" and "embedding (inlining) function
>> code at a specific call site" are two completely different, independent,
>> unrelated things. In your example you simply declared a function as
>
> Wikipedia says otherwise:
>
> https://en.wikipedia.org/wiki/Inline_function
>
> "It serves as a compiler directive that suggests (but does not require) that
> the compiler substitute the body of the function inline by performing inline
> expansion, i.e. by inserting the function code at the address of each function
> call,"

Wikipedia is a good source in general, and particularly for
computer-related stuff, but it isn't as authoritative as the C++
standard itself:

"... The inline specifier indicates to the implementation that inline
substitution of the function body at the point of call is to be
preferred to the usual function call mechanism. An implementation is not
required to perform this inline substitution at the point of call;
however, even if this inline substitution is omitted, the other rules
for inline functions specified in this subclause shall still be
respected. ..." (9.2.7p2)

Note that inline substitution, which he denies has anything to do with
the inline keyword, is in fact the primary thing that the description of
that keyword is concerned with. There's lots of other things that the
standard says about 'inline' in other sections of the standard, and it's
those other things that he's talking about, but all of those other
things are written merely to support the primary purpose of 'inline'.
There are other ways to get around the ODR rule that don't involve the
'inline' keyword.

> Perhaps you're the one who's getting confused?

He's not confused, he just doesn't approve of what the standard says
about "inline", and therefore deliberately misrepresents it. The
standard's wording above makes "inline" no more than a hint. An
implementation is free to perform inline substitution of any function
call, whether or not the function is declared 'inline', so long as the
result produces the same observable effects as calling a separate
function. Many implementations don't even implement 'inline' as a hint,
completely ignoring it when deciding whether or not to perform inline
substitution. That's why he pretends that the wording I've quoted above
doesn't exist.

The problem with your code is not that inline substitution isn't being
done (though that is a possibility), it's that you have incorrect
expectations about what the result of that substitution would be.
Whether inlined or not, the object named 's' is in a separate block from
the function call itself, and therefore has a lifetime that ends when
the end of that block is reached. Whether or not the function is
inlined, that occurs before your code uses the reference to 's'.

James Kuyper

unread,
Jan 14, 2022, 11:26:29 AM1/14/22
to
On 1/14/22 8:34 AM, James Kuyper wrote:
...
> standard's wording above makes "inline" no more than a hint. An
> implementation is free to perform inline substitution of any function
> call, whether or not the function is declared 'inline', so long as the
> result produces the same observable effects as calling a separate
> function.

I wanted to point out that the opposite transformation is also
permitted: and implementation can take code out of the function where it
is written, and replace it with a call to a separate function created by
the implementation containing that code.

Why would an implementation do this? For the opposite of the reason it
would inline some code. In general, inlining trades off an increase in
the execution speed against increased code size. There are important
exceptions: the inlined code could be smaller than the code that would
needed to implement the function call. Even if it isn't, inline
substitution could open up opportunities for optimization involving
interactions between the code inside the inlined function, and code
surrounding the function call. However, when neither of those cases
apply, each call to a function that gets inlined increases the size of
the generated code.

When I first mentioned this possibility, I had no idea whether any
existing compiler would do this, but I thought it unlikely. I was
corresponding surprised by a response that identified a particular
compiler that actually did it. As I might have anticipated, that
compiler was cross-compiling for a memory-starved embedded system.
Unfortunately, that was a decade ago, and I no longer remember any details.

Manfred

unread,
Jan 14, 2022, 11:30:54 AM1/14/22
to
Moreover, if someone wants to learn programming from Wikipedia, good
luck to them. But don't expect me to trust their code.

James Kuyper gave very clear explanations of the OP's mistake.

Mut...@dastardlyhq.com

unread,
Jan 14, 2022, 11:40:41 AM1/14/22
to
What do you think "inserting the function code at the address of each
function call" means?

Andrey Tarasevich

unread,
Jan 14, 2022, 11:43:55 AM1/14/22
to
No, I'm not. The wording in point 1 on the Wikipedia page (as well as
the text of the standard) clearly states that in this role keyword
`inline` is just a suggestion. The language standard says that this
suggestion can be freely ignored by the compilers.

That is sufficient to completely discard point 1 from consideration. I
don't really know why the standard still keeps it as a _normative_ part
of the text, while the wording itself is completely _informative_ in its
essence. This state of affairs only helps to fuel the confusion.

--

As a historical note, it is true that keyword `inline` was originally
introduced specifically to "suggest" inlining of function bodies at call
sites. But it quickly became clear that it is useless and unnecessary in
this role. As a consequence, its role was refocused to being a "ODR
defeater" - a completely different purpose.

In a way, saying that `inline` suggests inlining is like saying that
"lvalue" refers to left-hand side of assignment. Yes, we all know that
historically this is how it came to existence. But we also know that
this is factually incorrect in the standard language.

Mut...@dastardlyhq.com

unread,
Jan 14, 2022, 11:49:20 AM1/14/22
to
Wikipedia tends to range from the uselessly sparse to the uselessly complex.
The article on discrete fourier transforms for example is pages of dense
mathematics probably written by post grad students or lecturers trying to
out-do each other in how obtuse they can be. I wrote an implementation in C
including comments that was 20 lines of code. Go figure.


David Brown

unread,
Jan 14, 2022, 12:48:11 PM1/14/22
to
If you are interested in such transformations, gcc does them (given the
right flags, appropriate code, and perhaps additional information such
as LTO, profile-guided optimisation or "hot" and "cold" function
attributes). In particular, it can pull out rarely used parts of code
into a separate function and re-use it from more than one place, or put
it in a different section to improve cache usage, or in connection with
function cloning and constant propagation. (Imagine a function that
takes a boolean parameter. In some cases, you get better results if you
have two versions of the function generated, each with a fixed value for
that parameter, and the choice of the function to use is made at the
call site.) Functions can also be partially inlined.

How often the compiler does such transformations, and how effective they
are in practice, I have no idea.

Öö Tiib

unread,
Jan 14, 2022, 2:28:58 PM1/14/22
to
That means inlining. Does "suggests (but does not require) inlining" mean
that it will be inlined?

Alf P. Steinbach

unread,
Jan 14, 2022, 2:30:08 PM1/14/22
to
On 14 Jan 2022 17:40, Mut...@dastardlyhq.com wrote:
> What do you think "inserting the function code at the address of each
> function call" means?

That's the thing that's not required, but can be done.

Which it also can without the `inline` keyword.

Originally `inline` did have a strong hinting effect about machine code
inlining. Bjarne wanted to do away with evil macros, as an advantage of
C++, and he wrote about that (somewhere, which I don't recall, but
probably tcpppl). However, things change, and among the changes was the
emergence of more compilers than just Bjarne's, and standardization, and
changes in common practice, and today the original intent of `inline` is
for all purposes a very very non-binding shallow hint, one that one
ideally could rely on /not/ being considered, because one wants the
guaranteed effect of `inline` without any silly side-effects.

- Alf

Juha Nieminen

unread,
Jan 17, 2022, 12:39:10 AM1/17/22
to
Andrey Tarasevich <andreyta...@hotmail.com> wrote:
> As a historical note, it is true that keyword `inline` was originally
> introduced specifically to "suggest" inlining of function bodies at call
> sites. But it quickly became clear that it is useless and unnecessary in
> this role. As a consequence, its role was refocused to being a "ODR
> defeater" - a completely different purpose.

And as a side note, the C standard also added support for 'inline'
functions, except that it's a different and very weird version of it that
to this day I still don't fully understand. It's like an 'inline' that's
*not* an "ODR defeater". (It's so confusing that the vast, vast majority
of C programmers just write "static inline" to get around the confusing
part.)

David Brown

unread,
Jan 17, 2022, 2:12:24 AM1/17/22
to
Yes, "inline" functions in C are a bit odd, and the semantics are not
the same as in C++. As you say, the most common usage (and the only way
I use it) is to write "static inline" for functions to replace what
might pre-C99 have been a function-like macro. The "inline" here is not
actually very significant - any decent C compiler will make its own
decisions about actual inlining optimisations, so a plain "static"
function would have the same effect. To me, at least, the "static
inline" is a documentation of how you see the function and expect it to
be used.

<https://en.cppreference.com/w/c/language/inline>

To add to the complication, gcc had "inline" as an extension prior to
C99, and it had slightly different semantics. (A number of gcc C89
extensions became part of C99, sometimes with changes.) For "static
inline" functions there is no difference, but there are differences for
"extern inline" and other combinations. This reinforces the benefits of
sticking to "static inline" in C.

<https://gcc.gnu.org/onlinedocs/gcc/Inline.html>

Chris M. Thomasson

unread,
Jan 17, 2022, 2:51:41 AM1/17/22
to
On 1/16/2022 11:12 PM, David Brown wrote:
> On 17/01/2022 06:38, Juha Nieminen wrote:
>> Andrey Tarasevich <andreyta...@hotmail.com> wrote:
>>> As a historical note, it is true that keyword `inline` was originally
>>> introduced specifically to "suggest" inlining of function bodies at call
>>> sites. But it quickly became clear that it is useless and unnecessary in
>>> this role. As a consequence, its role was refocused to being a "ODR
>>> defeater" - a completely different purpose.
>>
>> And as a side note, the C standard also added support for 'inline'
>> functions, except that it's a different and very weird version of it that
>> to this day I still don't fully understand. It's like an 'inline' that's
>> *not* an "ODR defeater". (It's so confusing that the vast, vast majority
>> of C programmers just write "static inline" to get around the confusing
>> part.)
>>
>
> Yes, "inline" functions in C are a bit odd, and the semantics are not
> the same as in C++. As you say, the most common usage (and the only way
> I use it) is to write "static inline" for functions to replace what
> might pre-C99 have been a function-like macro. The "inline" here is not
> actually very significant - any decent C compiler will make its own
> decisions about actual inlining optimisations, so a plain "static"
> function would have the same effect. To me, at least, the "static
> inline" is a documentation of how you see the function and expect it to
> be used.
[...]

Imvvvho, using inline is almost akin to almost begging the compiler to
actually inline the function. It certainly can say f-you, and give the
programmer the proverbial middle finger at the same time! For some
reason it kind of makes me think of the register keyword...

Now, it would be funny if a compiler would output something like:

this function cannot be inlined, go ahead and try turning on link time
optimization, and try again? ;^)


Andrey Tarasevich

unread,
Jan 17, 2022, 2:53:45 AM1/17/22
to
Yes, you are right. `inline` is clearly an "ODR defeater" in C++, but in
C... not so much.

Firstly, `static inline` is a separate story. Most of the time `static`
is all that's needed. In a quality compiler `static inline` is always
redundant. It is 100% equivalent to plain `static`. There's never any
tangible reason to use `static` and `inline` together. (Unless one wants
to add `inline` for purely aesthetic reasons - to express how they feel
about that function.)

The only thing that compiler needs to be able to inline a function call
is access to the function's definition, to the full function's body.
Once it can see the body, it can analyze and inline the calls (if it
decides to inline). `static` functions are always defined inside their
TUs, which means that they are already as inlinable as they can ever be.
Declaring them `inline` on top of `static` achieves absolutely nothing.

So, the only tangible use of `inline` is with external linkage
functions. External linkage implies that if an inline function ends up
with a regular body, it should be one unique body in the whole program.
And, thanks to that, the function will have unique address identity:
same address in all TUs.

This is the objective. And this is where C and C++ take drastically
different paths to that objective.

C++ achieves the above automatically through "generate them all; let
linker sort them out" approach: compiler nonchalantly emits bodies for
external linkage inline functions in different TUs, then linker discards
all same-named bodies except one (see "weak symbols").

C, on the other hand, continues to stick to pedantically manual approach
to ODR: it is the user's responsibility to ensure existence and
uniqueness of a regular body generated for an inline function (just in
case that body becomes necessary). The user chooses the TU for that body
and the user triggers it by using the `extern inline` combination.

So, yes, in C `inline` is not really an "ODR defeater". `inline`
definitions in C do not emit function bodies, which means that there's
nothing to defeat. `extern inline` declaration does emit a function
body, but the burden of making it (and making it only once) is placed
upon the user.

Chris M. Thomasson

unread,
Jan 17, 2022, 2:55:49 AM1/17/22
to
Then, the programmer turns on link time optimization, and tries again.
The compiler says, well, shit happens! No inline for you! Better try asm.

Juha Nieminen

unread,
Jan 17, 2022, 4:51:16 AM1/17/22
to
David Brown <david...@hesbynett.no> wrote:
> Yes, "inline" functions in C are a bit odd, and the semantics are not
> the same as in C++. As you say, the most common usage (and the only way
> I use it) is to write "static inline" for functions to replace what
> might pre-C99 have been a function-like macro. The "inline" here is not
> actually very significant - any decent C compiler will make its own
> decisions about actual inlining optimisations, so a plain "static"
> function would have the same effect. To me, at least, the "static
> inline" is a documentation of how you see the function and expect it to
> be used.

The difference between a 'static' (or 'static inline') function and a
(non-static) 'inline' function is that in the former case the function
gets instantiated as many times as there are compilation units that
call it (or in every compilation unit that includes the header, regardless
of whether anything calls it or not, as some more primitive C compilers
do. Yes, they exist.) In the latter case the function is instantiated
in the executable binary only once, just like in C++ (but in C you have
to explicitly tell *where* it's instantiated, while in C++ it's
automatic.)

Since as far as I remember you cannot have 'static' variables inside
an 'inline' function (unlike in C++), the only possible situation that
I can think of where you have to make an 'inline' function non-static
is if the function needs to have a unique pointer (eg. if you for
some reason need to compare function pointers). Or if you need to
squeeze even the last bits out of the executable binary size and do
not want the function to be duplicated.

Sometimes you can get away with using (non-static) 'inline' without
specifying where the function should be instantiated (if the compiler
never generates an actual function call), but I think that's
non-standard behavior.

David Brown

unread,
Jan 17, 2022, 5:35:46 AM1/17/22
to
"inline" does not make such demands of the compiler - in C or C++. In
C++ it is very useful for letting you put definitions in headers (an
"ODR defeater", as some call it). In C, with decent compilers, it is
much less useful as you could use a non-inline "static" function in a
header and get pretty much the same effect as a "static inline"
function. To me, it is as much a documentation of the programmer
intention as anything else.

If you want to insist that the compiler really does inline a function,
you need to use compiler-specific features - like gcc/clang
"always_inline" attribute. If you want the compiler to complain when it
can't inline a function, gcc has "-Winline" (or "-Werror=inline" for an
error).

Bonita Montero

unread,
Jan 17, 2022, 6:40:44 AM1/17/22
to
Am 14.01.2022 um 02:51 schrieb Andrey Tarasevich:

> 2. Keyword `inline` has absolutely nothing to do with embedding function
> code at the call site. The purpose of `inline` keyword is to allow you
> to defeat ODR restrictions for a function (or a variable) with external
> linkage. ...

That's true for inline-variables but for inline-functions only parti-
tially. For inline functions inline is also a hint for the compiler
to inline the code in the calling code. But this is only a hint and
not mandantory.

Juha Nieminen

unread,
Jan 17, 2022, 7:55:39 AM1/17/22
to
Alf P. Steinbach <alf.p.s...@gmail.com> wrote:
> Some years ago g++ treated `inline` as an almost absolute inlining
> directive.

Does anyone have a reference or documentation that shows that compilers
like gcc, clang, icc and Visual Studio behave differently with respect
to inlining a function depending on whether the function has been marked
as 'inline' or not?

For the longest time I have got the impression that if a compiler sees
the function definition from the call location, it uses a heuristic to
determine whether it will inline the function or not, and this heuristic
completely ignores whether the function has been marked as 'inline'.
However, I could well be wrong. Maybe they use different heuristics
depending on whether that keyword appears or not?

David Brown

unread,
Jan 17, 2022, 9:14:52 AM1/17/22
to
That is correct, as far as I know. There are some flags that can
influence inlining, such as whether static functions that are only
called once are always inlined or not, but you rarely use them - enable
an appropriate level of optimisation (-O2 is good for many uses), and
the compiler will probably do a better job than the programmer would at
figuring out the best policy for each function. The compiler can also
be more flexible (such as partial inlining) than a programmer could specify.

If I want control over inlining (which I occasionally do in my embedded
development), I use gcc's "always_inline" or "noinline" attributes.

If Alf is correct about gcc treating "inline" as "an almost absolute
directive", it must have been a /long/ time ago - gcc 2.95 from pre-C99
days did heuristic-based inlining optimisations, and that's as far back
as I have bothered checking.

Andrey Tarasevich

unread,
Jan 17, 2022, 1:14:00 PM1/17/22
to
Um... Once again "only a hint to inline and not mandantory" is
completely meaningless wording within the context of a formal document.
Such wording should be relegated to a footnote, to explain the etymology
of the keyword.

Currently, this is essentially a defect in the standard, which only
serves to confuse people.

The proper wording describing the purpose and intent of `inline` should
say something along the lines of "the purpose of `inline` keyword is to
make the definition of a function with external linkage visible in all
translation units without triggering an ODR violation".

Once they've stated that, they can also add a bit of rationale, e.g.
"this helps facilitate inlining and many other useful optimizations"

Note, BTW, that when a full function body is visible to the compiler,
the optimization benefits of such visibility go far beyond inlining.

Andrey Tarasevich

unread,
Jan 17, 2022, 1:24:51 PM1/17/22
to
On 1/17/2022 4:55 AM, Juha Nieminen wrote:
> Alf P. Steinbach <alf.p.s...@gmail.com> wrote:
>> Some years ago g++ treated `inline` as an almost absolute inlining
>> directive.
>
> Does anyone have a reference or documentation that shows that compilers
> like gcc, clang, icc and Visual Studio behave differently with respect
> to inlining a function depending on whether the function has been marked
> as 'inline' or not?
>

Depends on the compiler settings. "By default" GCC considers for
inlining only those functions that are explicitly declared `inline`.

In order to inline something else one'd need to enable
`-finline-functions-called-once` (included in `-O1`),
`-finline-functions`, `-finline-small-functions` (included in `-O2`) and
so on.

David Brown

unread,
Jan 17, 2022, 1:46:58 PM1/17/22
to
The manual is a bit inconsistent here. Under optimisation options, it
says you need "-fno-inline" to turn off inlining of functions declared
"inline". Under "An Inline Function is As Fast As a Macro", it says
that no functions are inlined if optimisation is not enabled.

James Kuyper

unread,
Jan 17, 2022, 2:39:59 PM1/17/22
to
On 1/17/22 4:50 AM, Juha Nieminen wrote:
...
> Since as far as I remember you cannot have 'static' variables inside
> an 'inline' function (unlike in C++),

That only applies to inline functions with external linkage, and only
for modifiable objects. The same is true of objects with thread storage
duration. Such functions are also prohibited from containing a reference
to an identifier with internal linkage. (C standard 6.7.4p3)

A key difference between C and C++ that is relevant to this thread is
that, in C++, 'inline' is an ignorable hint that inline substitution
should be performed. In C, it's an ignorable hint that "that calls to
the function be as fast as possible.", with the method where by that
might be achieved being unspecified.

James Kuyper

unread,
Jan 17, 2022, 4:15:22 PM1/17/22
to
On 1/17/22 1:13 PM, Andrey Tarasevich wrote:
...
> Um... Once again "only a hint to inline and not mandantory" is
> completely meaningless wording within the context of a formal document.
> Such wording should be relegated to a footnote, to explain the etymology
> of the keyword.

You might disapprove of such a feature, but being a non-mandatory hint
is the primary purpose of this feature. It seems odd to me to mention
the primary purpose of a feature only in a footnote.

> The proper wording describing the purpose and intent of `inline` should
> say something along the lines of "the purpose of `inline` keyword is to
> make the definition of a function with external linkage visible in all
> translation units without triggering an ODR violation".

Could you show me how you would use inline for the purpose of violating
the ODR rules, where there's no more appropriate way to achieve the same
objective? Allowing you do do so was certainly not the purpose of 'inline'.

Tim Rentsch

unread,
Jan 17, 2022, 6:30:15 PM1/17/22
to
Andrey Tarasevich <andreyta...@hotmail.com> writes:

> On 1/16/2022 9:38 PM, Juha Nieminen wrote:
>
>> Andrey Tarasevich <andreyta...@hotmail.com> wrote:
>>
>>> As a historical note, it is true that keyword `inline` was
>>> originally introduced specifically to "suggest" inlining of
>>> function bodies at call sites. But it quickly became clear that
>>> it is useless and unnecessary in this role. As a consequence,
>>> its role was refocused to being a "ODR defeater" - a completely
>>> different purpose.
>>
>> And as a side note, the C standard also added support for
>> 'inline' functions, except that it's a different and very weird
>> version of it that to this day I still don't fully understand.
>> It's like an 'inline' that's *not* an "ODR defeater". (It's so
>> confusing that the vast, vast majority of C programmers just
>> write "static inline" to get around the confusing part.)
>
> Yes, you are right. `inline` is clearly an "ODR defeater" in C++,
> but in C... not so much.
>
> Firstly, `static inline` is a separate story. Most of the time
> static` is all that's needed. In a quality compiler `static
> inline` is always redundant. It is 100% equivalent to plain
> `static`. There's never any tangible reason to use `static` and
> `inline` together. [...]

In C++ I expect that's right. In C though there is a key
difference that may provide a reason to use 'inline'. In C
declaring or defining a function 'inline' can provide additional
guarantees beyond just using 'static'. In the semantics section
of 6.7.4 of the C standard, there is this excerpt:

Making a function an inline function suggests that calls to
the function be as fast as possible. The extent to which
such suggestions are effective is implementation-defined.

Note the second sentence. C implementations must document
what happens with 'inline', but there is no such requirement
for 'static'.

(I should add that I'm assuming that C++ does not impose a
similar requirement. I have not tried looking in the C++
standard to see if that is the case.)

Tim Rentsch

unread,
Jan 17, 2022, 6:35:03 PM1/17/22
to
Note the sentence in the semantics portion of 6.7.4 of the C
standard that says

The extent to which such suggestions are effective is
implementation-defined.

So I think "implementation-defined" is more accurate than
"unspecified".

james...@alumni.caltech.edu

unread,
Jan 17, 2022, 6:50:43 PM1/17/22
to
On Monday, January 17, 2022 at 6:35:03 PM UTC-5, Tim Rentsch wrote:
> James Kuyper <james...@alumni.caltech.edu> writes:
...
> > A key difference between C and C++ that is relevant to this thread is
> > that, in C++, 'inline' is an ignorable hint that inline substitution
> > should be performed. In C, it's an ignorable hint that "that calls to
> > the function be as fast as possible.", with the method where by that
> > might be achieved being unspecified.
> Note the sentence in the semantics portion of 6.7.4 of the C
> standard that says
> The extent to which such suggestions are effective is
> implementation-defined.
> So I think "implementation-defined" is more accurate than
> "unspecified".

"implementation-defined" behavior is unspecified behavior that an
implementation is required to document, so both terms are correct,
but "implementation-defined" is more specific. However, the point I
was making was about what the standard failed to specify, so
"unspecified" was relevant - that an implementation is required
to document the behavior wasn't.

Andrey Tarasevich

unread,
Jan 17, 2022, 7:28:27 PM1/17/22
to
On 1/17/2022 1:15 PM, James Kuyper wrote:
> On 1/17/22 1:13 PM, Andrey Tarasevich wrote:
> ...
>> Um... Once again "only a hint to inline and not mandantory" is
>> completely meaningless wording within the context of a formal document.
>> Such wording should be relegated to a footnote, to explain the etymology
>> of the keyword.
>
> You might disapprove of such a feature, but being a non-mandatory hint
> is the primary purpose of this feature. It seems odd to me to mention
> the primary purpose of a feature only in a footnote.

That is false. The primary purpose of this feature is, again, is
consistent between functions and variables: facilitate support for
multiple definitions of the the same entity with external linkage
(variable or function) across multiple TUs (see below)

>> The proper wording describing the purpose and intent of `inline` should
>> say something along the lines of "the purpose of `inline` keyword is to
>> make the definition of a function with external linkage visible in all
>> translation units without triggering an ODR violation".
>
> Could you show me how you would use inline for the purpose of violating
> the ODR rules, where there's no more appropriate way to achieve the same
> objective? Allowing you do do so was certainly not the purpose of 'inline'.

Um... This is some rather strange wording: "use inline for the purpose
of violating the ODR rules". Where did you get this? ODR rules,
obviously, are aware of `inline` and inclusive of `inline`. Nobody's
talking about "violating" them here in any formal way.

What I'm referring to is rather obvious from the above discussion.

The primary purpose of the feature is this: the whole problem, the whole
objective is provide us with a feature, that would let us write

unsigned foo = 42;

void bar()
{
}

in a header file and then include this file into multiple TUs.

A naive attempt to do this will result in ODR violations: multiple
definitions of entities with external linkage.

(`static` might be seen as workaround for the function, but we don't
want that. External linkage is the point here. For whatever reason, we
want a function with unique address identity for the whole program.)

So, we need a feature that will
1) suppress the ODR violations, and
2) ensure the unique address/storage identity for both the variable and
the function.

And (drum roll) that is achieved through `inline`

inline unsigned foo = 42;

inline void bar()
{
}

Done. End of story. And no point any "hints" or "embedding of function
call sites" come into this picture. They are completely irrelevant.

As a side note, of course, the reason we want this is to expose full
function body to the compiler in all TUs, thus helping the compiler to
fully analyze the function, optimize its usage and optimize surrounding
code based on its knowledge of function's internal behavior. The actual
"embedding of function call sites" is just one [minor] part of this
process.

How the compiler will implement the required spec is its own business,
but as we all know the most popular approach today is to just push the
problem over to the linker and let it silently eliminate the unnecessary
copies.

This implementational approach is what justifies referring to `inline`
as an "ODR defeater" informally. That's basically what `inline` does: it
tells the linker that instead of complaining about multiple definitions
it has to shut up and just mop things up quietly.

That is the prime purpose of `inline`. Has always been. All these
stories about the "hint" is just a mumbo-jumbo, which probably only
persists in standard text out of respect to someone who originally
introduced it.

James Kuyper

unread,
Jan 17, 2022, 10:55:43 PM1/17/22
to
On 1/17/22 7:28 PM, Andrey Tarasevich wrote:
...
> Um... This is some rather strange wording: "use inline for the purpose
> of violating the ODR rules". Where did you get this? ODR rules,
> obviously, are aware of `inline` and inclusive of `inline`. Nobody's
> talking about "violating" them here in any formal way.

You're talking about 'inline' as a way of getting around those rules. If
'inline' didn't exist, then those rules obviously couldn't cite it as an
exception, and what you're claiming is the primary purpose of 'inline'
would be a violation of those rules. That purpose is expressed by
inserting an exception for 'inline' into those rules.

> What I'm referring to is rather obvious from the above discussion.
>
> The primary purpose of the feature is this: the whole problem, the whole
> objective is provide us with a feature, that would let us write
>
> unsigned foo = 42;
>
> void bar()
> {
> }
>
> in a header file and then include this file into multiple TUs.

Declaring them with internal linkage would achieve the same benefit.

...
> (`static` might be seen as workaround for the function, but we don't
> want that. External linkage is the point here. For whatever reason, we
> want a function with unique address identity for the whole program.)

Why? I can imagine unusual circumstances where that might be needed, but
I wouldn't expect it to be a common need. Note that an inline function
is only required to have a unique address if it has external linkage or
module linkage (9.2.7p6). If having a single unique address was the main
purpose of 'inline', why would it even be permitted to declare an inline
function with internal linkage? You could have functions with internal
linkage if you don't need a unique address, and 'inline' functions, with
inherently external linkage, if you do need a unique address. If that's
the primary purpose, why didn't they it that way?

...
> That is the prime purpose of `inline`. Has always been. All these
> stories about the "hint" is just a mumbo-jumbo, which probably only
> persists in standard text out of respect to someone who originally
> introduced it.

That's a ridiculous suggestion. That's not how standards get written. If
getting around the ODR rules was even an important secondary purpose for
'inline', there would have been some mention of it somewhere in 9.2.7.

That ridiculous suggestion isn't even consistent with the immediately
preceding sentence. If the person who originally introduced it had a
different conception of the purpose, one that the standard pays only lip
service to, then by definition the purpose you refer to has not "always
been" the prime purpose. There had to have been at least a short period
of time (as I understand it, that "short" period of time has been
decades long) during which the purpose it originally was introduced for
remained the primary purpose.

Bonita Montero

unread,
Jan 18, 2022, 3:20:30 AM1/18/22
to
Am 17.01.2022 um 19:13 schrieb Andrey Tarasevich:
> On 1/17/2022 3:40 AM, Bonita Montero wrote:
>> Am 14.01.2022 um 02:51 schrieb Andrey Tarasevich:
>>
>>> 2. Keyword `inline` has absolutely nothing to do with embedding
>>> function code at the call site. The purpose of `inline` keyword is to
>>> allow you to defeat ODR restrictions for a function (or a variable)
>>> with external linkage. ...
>>
>> That's true for inline-variables but for inline-functions only parti-
>> tially. For inline functions inline is also a hint for the compiler
>> to inline the code in the calling code. But this is only a hint and
>> not mandantory.
>
> Um... Once again "only a hint to inline and not mandantory" is
> completely meaningless wording within the context of a formal document.
> Such wording should be relegated to a footnote, to explain the etymology
> of the keyword.
>
> Currently, this is essentially a defect in the standard, which only
> serves to confuse people.

You're simply an idiot who can't accept the world like it is.

Andrey Tarasevich

unread,
Jan 18, 2022, 2:32:25 PM1/18/22
to
On 1/17/2022 7:55 PM, James Kuyper wrote:
> On 1/17/22 7:28 PM, Andrey Tarasevich wrote:
> ...
>> Um... This is some rather strange wording: "use inline for the purpose
>> of violating the ODR rules". Where did you get this? ODR rules,
>> obviously, are aware of `inline` and inclusive of `inline`. Nobody's
>> talking about "violating" them here in any formal way.
>
> You're talking about 'inline' as a way of getting around those rules. If
> 'inline' didn't exist, then those rules obviously couldn't cite it as an
> exception, and what you're claiming is the primary purpose of 'inline'
> would be a violation of those rules. That purpose is expressed by
> inserting an exception for 'inline' into those rules.

When I'm talking about `inline` as an "ODR suppressor" or "defeater",
I'm not talking about the exact formal ODR as it is defined in C++
standard. I'm referring to the general/basic/primitive linker-level idea
of ODR: "if you have two identical symbols in you object files, you end
up with multiple definition error from the linker".

>> What I'm referring to is rather obvious from the above discussion.
>>
>> The primary purpose of the feature is this: the whole problem, the whole
>> objective is provide us with a feature, that would let us write
>>
>> unsigned foo = 42;
>>
>> void bar()
>> {
>> }
>>
>> in a header file and then include this file into multiple TUs.
>
> Declaring them with internal linkage would achieve the same benefit.

No it won't. In that case they won't have external linkage, meaning that
they will not refer to the same entity everywhere in the program. I
think I made it clear that this is part of the objective as well.

> ...
>> (`static` might be seen as workaround for the function, but we don't
>> want that. External linkage is the point here. For whatever reason, we
>> want a function with unique address identity for the whole program.)
>
> Why? I can imagine unusual circumstances where that might be needed, but
> I wouldn't expect it to be a common need.

For variables the common need is obvious. (I'm surprised I have to even
mention it.)

At the same time, I agree that for functions it is much less important
and common.

But if this were just about inline functions, chances are nobody would
even bother. However, in C++ the primary driver for this functionality
is not really inline functions at all. It is templates. 99.9% of the
pressing need for this "ODR suppressing" functionality comes from
templates. Implicit instantiation of templates faces the very same
issues: external linkage and multiple definitions, conflicting with the
"basic" idea of ODR I described above. Templates are the primary driver
behind that "gratuitously-overgenerate-then-discard" approach relied
upon by modern implementations.

(There were alternative implementations, intended to avoid
"overgeneration" and to play nice with "basic ODR", e.g. Sun Solaris C++
compiler, but AFAIK none of them survived.)

(C++ also provides you with facilities for explicit "manual"
instantiation of templates, which are, curiously, quite similar to C's
approach to `inline`. But I hope it is clear to everyone that C++
templates would never take off without their _implicit_ instantiation
mechanics.)

So, whether you consider inline functions with external linkage
necessary or not is not really that important. The
"gratuitously-overgenerate-then-discard" approach would still be there
anyway - for templates. And once it's there, you basically get inline
functions with external linkage for free, just for the sake of formal
completeness.

> Note that an inline function
> is only required to have a unique address if it has external linkage or
> module linkage (9.2.7p6). If having a single unique address was the main
> purpose of 'inline', why would it even be permitted to declare an inline
> function with internal linkage?

"Having a single unique out-of-line body" is probably better wording.
Having a single unique address naturally comes with it.

As for why inline functions with internal linkage are permitted...
There's simply no reason to prohibit it. `static inline` is redundant,
but there's no reason to spend effort to introduce a rule that would
prohibit it. There lots and lots of such redundant yet legal constructs
in C++.

> You could have functions with internal
> linkage if you don't need a unique address, and 'inline' functions, with
> inherently external linkage, if you do need a unique address. If that's
> the primary purpose, why didn't they it that way?

Again, that would lead to a more complicated specification of this
specifier without any tangible benefits. No need to do that.

Here's an immediately available example for you: variables again. Note
that `static inline` is _obviously_ redundant with namespace-scope
variables. There's no need to involve any "woulds", "whatifs" or
contrived scenarios here. `static inline` is obviously and
unconditionally redundant with variables today, right now, everywhere.
Yet, this is perfectly legal

/* Namespace scope */
static inline unsigned n = 42;

You can start asking your "why?" questions now. "Why do they allow
this?" "Why is it legal?" To me the answer is natural and obvious: why
not? No harm done, no need to overcomplicate things. This has always
been one of the cornerstone principles in thois language's design.

James Kuyper

unread,
Jan 18, 2022, 7:03:45 PM1/18/22
to
On 1/18/22 2:32 PM, Andrey Tarasevich wrote:
...
> You can start asking your "why?" questions now. "Why do they allow
> this?" "Why is it legal?" To me the answer is natural and obvious: why
> not? No harm done, no need to overcomplicate things. This has always
> been one of the cornerstone principles in thois language's design.

When you're claiming that what it actually says in 9.2.1p2 is
irrelevant, and stuff that it says nowhere in 9.2.7 is actually the
primary purpose of the 'inline' specifier, then you're going to need
better arguments than those. Having confirmed that you don't have any,
I'm going to leave this discussion - it's not going anywhere.

Andrey Tarasevich

unread,
Jan 18, 2022, 8:22:19 PM1/18/22
to
Sigh... It appears that I'm trying to prove that Earth is round at a
meeting of Flat Earth Society. "You're going to need better arguments
than those", they say... )))

Sorry, boys, but to prevent any further time-wasting I'm going to have
to pull the rank here: at this point just study, take notes and commit
to memory what I said above. If you feel that you need extra
rationale/argumentation to better understand the material - you'll find
all that later (leave to you as an exercise).

Tim Rentsch

unread,
Apr 25, 2022, 6:43:03 AM4/25/22
to
On the contrary, it is quite relevant. The requirement of being
documented means a developer can read the documentation and rely on
a particular behavior, and that is a huge difference.

james...@alumni.caltech.edu

unread,
Apr 25, 2022, 11:48:59 AM4/25/22
to
Agreed. It is a huge difference. It is also a difference that is irrelevant to
the point I was making. I was not talking about whether or not a developer
could find out what the behavior was. I was only talking about whether or
not the standard specified a particular behavior, rather than a set
containing two or more permitted behaviors.

0 new messages