对内联行为C++的困惑

Ant*_*Ant 1 c++

我正在学习C++,我对内联行为感到困惑.在cppreference上,我发现"包含在多个源文件中的函数必须是内联的".他们的例子如下:

// header file
#ifndef EXAMPLE_H
#define EXAMPLE_H
// function included in multiple source files must be inline
inline int sum(int a, int b) 
{
    return a + b;
}
#endif

// source file #2
#include "example.h"
int a()
{
    return sum(1, 2);
}

// source file #1
#include "example.h"
int b()
{
    return sum(3, 4);
}
Run Code Online (Sandbox Code Playgroud)

这对我来说有点混乱 - 我认为ifndef后卫完全正在做这项工作,即在多次包含同一文件时防止出现问题.无论如何,我想测试一下,所以我准备了以下内容:

// Sum.h
#ifndef SUM_H
#define SUM_H
int sum(int a, int b);
#endif

// Sum.cpp
int sum(int a, int b){
    return a + b;
}

// a.h
#ifndef A_H
#define A_H
int af();
#endif

// a.cpp
#include "sum.h"

int af(){
    return sum(3, 4);
}

// b.h
#ifndef B_H
#define B_H
int bf();
#endif 

// b.cpp
#include "sum.h"

int bf(){
    return sum(1, 2);
}

// main.cpp
#include "sum.h"
#include "a.h"
#include "b.h"
#include <iostream>
int main() {
    std::cout << af() + bf();
}
Run Code Online (Sandbox Code Playgroud)

这可以正常工作.然后我使用sum函数定义sum.cpp和sum.h中的内联,并且无法编译:

"sum(int, int)", referenced from:
      bf() in b.cpp.o
      af() in a.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Run Code Online (Sandbox Code Playgroud)

有人可以为我澄清一下吗?

Kla*_*aus 5

在包括与警卫#ifndef/#define/#endif#pragma once只是阻止列入为每一个翻译单元.

让我们假设你有一个像这样的标题:

#pragma once

void Do() {}
Run Code Online (Sandbox Code Playgroud)

以及两个*.cpp包含的文件.如果你现在编译

g++ source1.cpp source2.cpp 
Run Code Online (Sandbox Code Playgroud)

你会得到一个

xy行/文件中Do()的多重定义

每个源文件将"单独"编译,因此从第一个设置的第二个翻译单元看不到警卫.两种翻译(汇编)都是完全独立的.因此,包括守卫在这种情况下不会保护任何东西.

为此,可以将函数定义定义为inline.现在两个定义都将呈现给链接器,但标记为"弱".链接器现在没有抱怨这两个定义,只接受其中一个(通常是最后一个!)并不重要,因为两者在这种情况下是相同的.

所以包含守卫有意义,你可以多次将文件包含到一个翻译单元.只有当您间接包含标题时,才会发生这种情况.让我们说啊有函数定义和bh和ch各包括啊如果你的cpp文件包含现在bh和ch都包含啊所以你有没有包含警戒的多个定义.这是包括警卫的用例.

如果函数已定义inline但在所有使用翻译单元中都不可见,则具有"未定义符号"的相反问题:

例:

拥有该文件:

inline void sum() {}
void f1() { sum(); }
Run Code Online (Sandbox Code Playgroud)

并用-O0yield生成,输出nm f1.o|c++filt

0000000000000000 T f1()
0000000000000000 W sum()
Run Code Online (Sandbox Code Playgroud)

我们将符号sum定义为弱,因此它可以在链接阶段多次出现.如果没有"看到"定义的第二个翻译单元将被链接而没有问题,也将使用它.

但是使用"-O3"你会得到:

0000000000000000 T f1()
Run Code Online (Sandbox Code Playgroud)

这是编译器特有的实现!编译器可以提供内联函数.如果使用更高的优化级别,通常它们不会.

作为一项规则:如果定义了一个函数inline,它的定义必须在每个翻译单元使用之前都可见!