简体   繁体   中英

Can I inline a function which uses a static variable?

I have this code:

// my.h

#ifndef MY_HEADER
#define MY_HEADER

int create_uid();

#endif
// my.cpp

#include "my.h"

static int _next_uid = 0;

int create_uid()
{
    return _next_uid++;
}

I want to inline create_uid() , while keeping the _next_uid variable global to the program so the variable is unique.

My questions are:

  1. Can I do this?
  2. Is the inline statement require _next_uid to be visible outside the compilation unit?

Note: This doesn't seems to answer those questions clearly.

Edited after clarifiation of the question.

If you want only a single _next_uid , then simply put the following into your header file:

inline int create_uid()
{
  static int _next_uid = 0;
  return _next_uid++;
}

Short answer. No. The following code

// my.h

static int _next_uid = 0;

inline int create_uid() {
    return _next_uid++;
}

will probably compile, but will result in undefined behaviour if used in more than one translation units. This is because the _next_uid variables are different entities in different translation units. Thus the definitions of create_uid() are also different. However:

If an inline function [...] with external linkage is defined differently in different translation units, the behavior is undefined. [1]

What you can do instead is either use a local scope static variable in the function, like @DanielLangr showed in one of the other answers [1]. This has the disadvantage, that that variable cannot be accessed outside of the function. Alternatively as @wohlstad mentioned in one of the comments, you can use a C++17 inline variable:

// my.h
inline int _next_uid = 0;

inline int create_uid() {
    return _next_uid++;
}

Note that this does not define a static variable. Using static and inline will have the same effect as just using static [3], which results in the undefined behaviour I mentioned above.

Inlining a function means per definition, that all the variables it uses must be reachable from the translation unit where it is inlined. This cannot work with a unique static (thus not visible to other TUs) variable.

[1]: https://en.cppreference.com/w/cpp/language/inline
[2]: https://stackoverflow.com/a/72124623/17862371
[3]: https://stackoverflow.com/a/58101307/17862371

Summary:

It doesn't work if you put implementation of inline next_id() to a single c file, which means the function is in a single compile unit. So main cannot find inline next_id() , you'll get the error undefined reference .

It can be compiled if you declare inline next_id() in a shared header file, in which case each compile unit will properly find inline next_id() .

In my case, only one instance of this global variable will appear in virtual address space .DATA segment of the process. The output number is continuous.

Example:

Makefile 8:

all:
    c++ -c main.cpp
    c++ -c second.cpp
    c++ -c share.cpp
    c++ main.o second.o share.o -o main

clean:
    rm -f main.o second.o share.o main

main.cpp 12:

#include <cstdio>
#include "share.hpp"
#include "second.hpp"

int main(){
    printf("[main] %d\n", next_id());
    consume_id();
    printf("[main] %d\n", next_id());
    consume_id();
    printf("[main] %d\n", next_id());
    return 0;
}

second.hpp 1:

void consume_id();

second.cpp 7:

#include <cstdio>

#include "share.hpp"

void consume_id(){
    printf("[scnd] %d\n", next_id());
}

share.hpp 4:

#pragma once

int next_id();

share.cpp 7:


static int _next_id = 0;

int next_id()
{
    return _next_id++;
}

Result output:

[main] 0
[scnd] 1
[main] 2
[scnd] 3
[main] 4

But if it is changed to:

share.cpp 4:

inline int next_id()
{
    return _next_id++;
}

undefined reference to `next_id()'

If changed to

share.hpp 7:

#pragma once
static int _next_id = 0;

inline int next_id()
{
    return _next_id++;
}

Works

EDIT :

It seems to be an undefined behavior

I'm using `gcc version 11.2.0 (Ubuntu 11.2.0-19ubuntu1)

IN MY CASE

You will have copies of static int _next_id but only in the object file. In memory there is only one.

objdump -d main > main.s

main.s 143:

00000000000011b3 <_Z7next_idv>:
    11b3:   f3 0f 1e fa             endbr64 
    11b7:   55                      push   %rbp
    11b8:   48 89 e5                mov    %rsp,%rbp
    11bb:   8b 05 53 2e 00 00       mov    0x2e53(%rip),%eax        # 4014 <_ZL8_next_id>
    11c1:   8d 50 01                lea    0x1(%rax),%edx
    11c4:   89 15 4a 2e 00 00       mov    %edx,0x2e4a(%rip)        # 4014 <_ZL8_next_id>
    11ca:   5d                      pop    %rbp
    11cb:   c3                      ret    

Here function _Z7next_idv only appears in memory for 1 time.

main.s 147:

    11bb:   8b 05 53 2e 00 00       mov    0x2e53(%rip),%eax        # 4014 <_ZL8_next_id>

The label of _next_id is _ZL8_next_id , only appears in memory for 1 time as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM