inline 不能修饰一个全局函数呗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
amiwrong123
V2EX    C++

inline 不能修饰一个全局函数呗?

  •  
  •   amiwrong123 2022-01-14 00:58:37 +08:00 3477 次点击
    这是一个创建于 1433 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我知道一般操作是 static inline 加函数定义放到头文件里。

    first.c

    inline void functionName(int first, int secend) {/****/ } 

    second.c

    extern void functionName(int first, int secend; int main() { functionName(1, 2); return 0; } 

    上面两个文件这么写,在 vs2019 上会报错。但是我看一些博客上说,是可以让 inline 修饰非 static 的全局函数。

    所以我这么用是错的呗?

    20 条回复    2022-01-15 10:00:45 +08:00
    wudicgi
        1
    wudicgi  
       2022-01-14 01:14:42 +08:00
    > 我知道一般操作是 static inline 加函数定义放到头文件里。
    那为什么不按这个来呢

    如果一定要把函数代码写在 .c 里,就去掉 first.c 里函数定义的 inline 修饰符好了,
    让编译器决定什么时候内联,什么时候调用

    又要内联,又要在其他 .c 里调用,这让编译器很难办啊
    Buges
        2
    Buges  
       2022-01-14 01:21:28 +08:00 via Android   1
    既要 inline ,又要 extern ,你这是为难编译器吧。
    wevsty
        3
    wevsty  
       2022-01-14 02:02:57 +08:00
    inline 编译器要求随时能找到代码,不然怎么给你内联进去。
    catror
        4
    catror  
       2022-01-14 02:22:45 +08:00 via Android
    inline 函数展开后符号都没了,extern 自然就没意义了。
    dangyuluo
        5
    dangyuluo  
       2022-01-14 04:23:39 +08:00
    什么时候一般操作就是 static inline 函数放在头文件里了。。
    codehz
        6
    codehz  
       2022-01-14 08:12:27 +08:00 via Android
    C++的 inline 语义并不是内联函数(内联是副作用)
    主要作用是允许同符号在多个翻译单元中多次出现(然后假设每次都一致),同时 inline 也要求必须在同翻译单元找到定义(除非没用到)
    extern inline 实际上按标准只能做同一个翻译单元里的前向声明。
    而在不同翻译单元混用 extern (无 inline )和 inline 实际上属于未定义行为,因为不符合前面所说的定义一致原则
    GeruzoniAnsasu
        7
    GeruzoniAnsasu  
       2022-01-14 08:17:53 +08:00
    首先…………………………

    extern 是「外部声明」,它的作用是告诉链接器 (!!) 注意不是编译器,到其它文件去找这个符号。如果 extern 直接放在定义的地方,则是告诉链接器「我这个符号应该让所有地方都能看得到」。

    但你这里 extern 只是一个声明。


    第二件事,inline 的意义在于告诉编译器 (!!) 注意是编译器,我这个符号的定义可能会重复出现很多次(因为会被 include 到不同文件里去),你就直接把找到的定义照抄放到调用点就行,不用暴露,不会有文件要链接我。




    所以你正在试图一边让链接器去找某个符号,一边让编译器不要暴露这个符号以供链接……




    「 inline 的非 static 全局函数」是这样的:

    a.h
    inline void g_f(){}

    a.c
    #include "a.h"
    void a(){g_f();}

    b.c
    #include "a.h"
    void b(){g_f();}

    main.c
    extern void a();
    extern void b();
    int main() {a();b();}
    iceheart
        8
    iceheart  
       2022-01-14 08:24:34 +08:00 via Android
    声明与定义不一致。
    访问 extern 只要一个声明,访问 inline 需要在同一个编译单元内。
    Jabin
        9
    Jabin  
       2022-01-14 08:50:50 +08:00 via Android
    加了 inline ,编译器也不一定按 inline 函数处理
    nightwitch
        10
    nightwitch  
       2022-01-14 09:18:57 +08:00
    static inline 其实就是等于 static ,含义是被 static 修饰的函数 /变量在每个编译单元内部可见,如果在头文件里的话每个编译单元都会复制一份。
    inline 现在的含义是允许一个函数 /变量在多个编译单元重复定义,在链接的时候会随机挑选一个符号链上。
    这两个关键词出现在头文件的时候,含义是截然不同的。
    lovelylain
        11
    lovelylain  
       2022-01-14 09:19:35 +08:00 via Android
    static 作用是把符号限制在本编译单元,这样同名函数多个编译单元定义也不会报错; inline 作用是建议编译器内联展开,但是编译器也可能不内联,如果你想函数体
    lovelylain
        12
    lovelylain  
       2022-01-14 09:29:33 +08:00 via Android
    static 作用是把符号限制在本编译单元,这样同名函数多处定义也不会报错; inline 作用是建议编译器内联展开,但是编译器也可能不内联,如果内联了,当然不存在符号问题,没内联的话,相当于会在.o 里生成一个弱符号函数,链接时同名的只会取一份。所以按需
    lovelylain
        13
    lovelylain  
       2022-01-14 09:41:15 +08:00 via Android
    static 作用是把符号限制在本编译单元,这样同名函数多处定义也不会报错; inline 作用是建议编译器内联展开,但是编译器也可能不内联,如果内联了,当然不存在符号问题,没内联的话,相当于会在.o 里生成一个弱符号函数,链接时同名的只会取一份。所以按需选择吧:
    如果你定义在.c 文件里面,建议 static inline ,因为如果其他.c 有跟你同名但不同实现的 inline ,是有风险的;
    如果你定义在.h 里,建议 inline ,在保证只有这个.h 里 inline 定义该函数的前提下,可以使没 inline 时共享同一份实现,减小文件体积,当然你怕出错用 static inline 也行;
    还有个 extern inline ,会在.o 里生成一个强符号,也有用处,但也容易出错,几乎没人用。
    agagega
        14
    agagega  
       2022-01-14 09:52:13 +08:00
    你这两个地方声明都不一样啊。不过 C 标准里是有 extern inline 这种东西的
    edimetia3d
        15
    edimetia3d  
       2022-01-14 12:52:03 +08:00
    我其实并不记得所有的语言规范细节了,我只记得 C 和 C++不一致, 不同编译器可能不一致,不同语言版本可能不一致.

    我脑海里记下的结论为:

    To live simpler, always use `static inline`

    针对你这个问题, C++ 要求 inline 的函数在每个编译单元都是 inline 且可见的.
    amiwrong123
        16
    amiwrong123  
    OP
       2022-01-14 22:10:39 +08:00
    @GeruzoniAnsasu #7
    没想到你这个居然编译通过了,那感觉你这个 inline 就好像起到了 static 的作用呢??因为两处的 g_f(){}的符号应该是一样的应该出产生冲突的,但是却没有。

    然后我根据你的这个程序,写了下面这个:
    first.cpp
    ```cpp
    #include <iostream>
    using namespace std;

    inline void g_f() {
    cout << "first" << endl;
    }

    void a() {
    cout << "a" << endl;
    g_f();
    }
    ```

    second.cpp
    ```cpp
    #include <iostream>
    using namespace std;

    inline void g_f() {
    cout << "second" << endl;
    }

    extern void a();
    void b() {
    cout << "b" << endl;
    g_f();
    }

    int main()
    {
    a();
    b();
    return 0;
    }
    ```
    然后打印出来这个,这个怎么解释呢?
    a
    second
    b
    second
    amiwrong123
        17
    amiwrong123  
    OP
       2022-01-14 22:25:20 +08:00
    @codehz #6
    老哥你看一下我上面那个程序,我这个程序应该就是你说的:允许同符号在多个翻译单元中多次出现
    g_f 函数在两个翻译单元都出现了。

    本来我以为会打印出来
    a
    first
    b
    second
    但是却不是。

    我不知道该怎么解释我的程序结果了
    amiwrong123
        18
    amiwrong123  
    OP
       2022-01-14 22:27:35 +08:00
    @jobmailcn #13
    老哥你看一下我上面那个程序,我这个程序应该就是你说的:如果内联了,当然不存在符号问题。

    但是还是有点不明白,调用 a()的时候,不应该用的是 first.cpp 里的函数定义吗,怎么还是用的 second.cpp 里的函数定义。
    amiwrong123
        19
    amiwrong123  
    OP
       2022-01-14 22:55:09 +08:00
    first.cpp
    ```cpp
    #include <iostream>
    using namespace std;

    inline void g_f() {
    cout << "first" << endl;
    }

    void a() {
    cout << "a" << endl;
    g_f();
    }
    extern void b();

    int main()
    {
    a();
    b();
    return 0;
    }
    ```

    second.cpp
    ```cpp
    #include <iostream>
    using namespace std;

    inline void g_f() {
    cout << "second" << endl;
    }

    void b() {
    cout << "b" << endl;
    g_f();
    }
    ```

    我把 main 函数放到 first.cpp 里,也是一样。都是打印 second 。不过是在 VS2019 里。

    然后我用 Ubuntu 下的 gcc 又试了一下,main 函数分别 first 里或 second 里,却是都打印的是 first 了。看来是一个随机的情况,也就是楼上说的“inline 现在的含义是允许一个函数 /变量在多个编译单元重复定义,在链接的时候会随机挑选一个符号链上。”
    codehz
        20
    codehz  
       2022-01-15 10:00:45 +08:00 via Android
    @amiwrong123 注意括号 然后假设每次都一致,这是前提条件。。。
    来看标准吧,c++20 标准 6.3 节 13 小节
    There can be more than one definition of a
    ... -inline function or variable ([dcl.inline]), ...
    这是允许 inline 函数或变量有多个定义(即使同一个 TU )再看需要满足的要求
    (13.8)
    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.
    每个定义都需要有相同的 token 序列
    (13.9)
    In each such definition, corresponding names, looked up according to [basic.lookup], shall refer to the same entity, after overload resolution ([over.match]) and after matching of partial template specialization ([temp.over]), except that a name can refer to ...... same entity in all definitions of D.
    每个定义所引用的符号在重载解析,模板偏特化之后除了某些特殊情况外都必须解析到相同的实体上
    最后
    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
    如果这些条件没有满足要求,那么程序是不合法的,而也只有特殊情况(同一个模块且在不一致定义要在同一个单元)才要求编译器给出诊断(报告错误)
    所以一个不要求诊断的错误的代码(甚至不属于 UB )给出任何行为都是符合预期的(链接期随意选择是结果的一部分而不是原因,写程序不是自然科学,不要用实验来推测原因)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     893 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 35ms UTC 21:09 PVG 05:09 LAX 13:09 JFK 16:09
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86