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

2022-01-14 00:58:37 +08:00
 amiwrong123

我知道一般操作是 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 的全局函数。

所以我这么用是错的呗?

2758 次点击
所在节点    C++
20 条回复
wudicgi
2022-01-14 01:14:42 +08:00
> 我知道一般操作是 static inline 加函数定义放到头文件里。
那为什么不按这个来呢

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

又要内联,又要在其他 .c 里调用,这让编译器很难办啊
Buges
2022-01-14 01:21:28 +08:00
既要 inline ,又要 extern ,你这是为难编译器吧。
wevsty
2022-01-14 02:02:57 +08:00
inline 编译器要求随时能找到代码,不然怎么给你内联进去。
catror
2022-01-14 02:22:45 +08:00
inline 函数展开后符号都没了,extern 自然就没意义了。
dangyuluo
2022-01-14 04:23:39 +08:00
什么时候一般操作就是 static inline 函数放在头文件里了。。
codehz
2022-01-14 08:12:27 +08:00
C++的 inline 语义并不是内联函数(内联是副作用)
主要作用是允许同符号在多个翻译单元中多次出现(然后假设每次都一致),同时 inline 也要求必须在同翻译单元找到定义(除非没用到)
extern inline 实际上按标准只能做同一个翻译单元里的前向声明。
而在不同翻译单元混用 extern (无 inline )和 inline 实际上属于未定义行为,因为不符合前面所说的定义一致原则
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
2022-01-14 08:24:34 +08:00
声明与定义不一致。
访问 extern 只要一个声明,访问 inline 需要在同一个编译单元内。
Jabin
2022-01-14 08:50:50 +08:00
加了 inline ,编译器也不一定按 inline 函数处理
nightwitch
2022-01-14 09:18:57 +08:00
static inline 其实就是等于 static ,含义是被 static 修饰的函数 /变量在每个编译单元内部可见,如果在头文件里的话每个编译单元都会复制一份。
inline 现在的含义是允许一个函数 /变量在多个编译单元重复定义,在链接的时候会随机挑选一个符号链上。
这两个关键词出现在头文件的时候,含义是截然不同的。
lovelylain
2022-01-14 09:19:35 +08:00
static 作用是把符号限制在本编译单元,这样同名函数多个编译单元定义也不会报错; inline 作用是建议编译器内联展开,但是编译器也可能不内联,如果你想函数体
lovelylain
2022-01-14 09:29:33 +08:00
static 作用是把符号限制在本编译单元,这样同名函数多处定义也不会报错; inline 作用是建议编译器内联展开,但是编译器也可能不内联,如果内联了,当然不存在符号问题,没内联的话,相当于会在.o 里生成一个弱符号函数,链接时同名的只会取一份。所以按需
lovelylain
2022-01-14 09:41:15 +08:00
static 作用是把符号限制在本编译单元,这样同名函数多处定义也不会报错; inline 作用是建议编译器内联展开,但是编译器也可能不内联,如果内联了,当然不存在符号问题,没内联的话,相当于会在.o 里生成一个弱符号函数,链接时同名的只会取一份。所以按需选择吧:
如果你定义在.c 文件里面,建议 static inline ,因为如果其他.c 有跟你同名但不同实现的 inline ,是有风险的;
如果你定义在.h 里,建议 inline ,在保证只有这个.h 里 inline 定义该函数的前提下,可以使没 inline 时共享同一份实现,减小文件体积,当然你怕出错用 static inline 也行;
还有个 extern inline ,会在.o 里生成一个强符号,也有用处,但也容易出错,几乎没人用。
agagega
2022-01-14 09:52:13 +08:00
你这两个地方声明都不一样啊。不过 C 标准里是有 extern inline 这种东西的
edimetia3d
2022-01-14 12:52:03 +08:00
我其实并不记得所有的语言规范细节了,我只记得 C 和 C++不一致, 不同编译器可能不一致,不同语言版本可能不一致.

我脑海里记下的结论为:

To live simpler, always use `static inline`

针对你这个问题, C++ 要求 inline 的函数在每个编译单元都是 inline 且可见的.
amiwrong123
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
2022-01-14 22:25:20 +08:00
@codehz #6
老哥你看一下我上面那个程序,我这个程序应该就是你说的:允许同符号在多个翻译单元中多次出现
g_f 函数在两个翻译单元都出现了。

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

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

但是还是有点不明白,调用 a()的时候,不应该用的是 first.cpp 里的函数定义吗,怎么还是用的 second.cpp 里的函数定义。
amiwrong123
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
2022-01-15 10:00:45 +08:00
@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 )给出任何行为都是符合预期的(链接期随意选择是结果的一部分而不是原因,写程序不是自然科学,不要用实验来推测原因)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/828136

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX