C++ 内联c++;组装中的方法

C++ 内联c++;组装中的方法,c++,assembly,inline,C++,Assembly,Inline,让我们创建一个名为myClass的小类。我感兴趣的是,当方法是内联的与否时,.asm中的差异是什么。我制作了两个程序,在cpp文件中使用和不使用内联关键字,但是.asm输出是相同的。我知道内联只是编译器的一个提示,我很有可能是优化的牺牲品,但是在asm中内联和非内联方法的一个小型cpp示例中,有可能看到它们之间的区别吗 h: cpp: 通用条款: 两种情况下的asm输出: .section __TEXT,__text,regular,pure_instructions .b

让我们创建一个名为myClass的小类。我感兴趣的是,当方法是内联的与否时,.asm中的差异是什么。我制作了两个程序,在cpp文件中使用不使用内联关键字,但是.asm输出是相同的。我知道内联只是编译器的一个提示,我很有可能是优化的牺牲品,但是在asm中内联和非内联方法的一个小型cpp示例中,有可能看到它们之间的区别吗

h:

cpp:

通用条款:

两种情况下的asm输出:

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 14
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    leaq    -8(%rbp), %rdi
    movl    $0, -4(%rbp)
    callq   __ZNK7myClass4getAEv
    xorl    %ecx, %ecx
    movl    %eax, -12(%rbp)         ## 4-byte Spill
    movl    %ecx, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function

.subsections_via_symbols

为了可靠地内联函数,函数的完整定义必须在调用出现的转换单元中可用

由于
myClass::getA()
的定义对
main.c
不可见,因此该函数不能内联。它可以内联到出现在
myClass.cpp
中的任何调用中,但您没有这些调用

要允许内联此函数,您需要在头文件中将其声明为
inline
,并包含其定义,例如

class myClass {
  …
  inline int getA() const { return a; }
  …
}

inline
关键字与告诉编译器内联调用函数关系不大。
inline
所做的是允许在头中内联定义函数

在函数定义中使用
inline
关键字可以在多个翻译单元中定义函数,只要所有定义相同,就不会违反一个定义规则


在头中定义内联函数可以帮助编译器内联调用函数,因为它允许在多个翻译单元中看到完整的定义,但这就是内联关键字与内联调用有关的全部内容。

您没有给编译器内联
myClass::getA()
(如另一个答案所述)。要比较内联方法和外联方法,请比较下面代码中
getA()
getAlt()
的用法

// file.h    
class myClass
{
    int a=1;
public:
    int getA() const { return a; }   // implicitly inline
    int getAlt() const;
};

// file.cc
#include "file.h"
int myClass::getAlt() const
{ return a; }

// main.cc
#include "file.h"
int main()
{
    myClass x;
    return x.getA() - x.getAlt();
}

您可以使用除过程间优化(ipo;因为这可能允许编译器甚至内联
myClass::getAlt()
)之外的全部优化。

gcc-O0
不启用
-finline函数
,因此即使函数在同一文件中,它也不会尝试。另请参阅。(不要费心尝试使用
\uuuuuu属性((始终为内联))
:你会得到内联,事情不会优化

您可以使用
gcc-O3-fwhole程序*.cpp
将内容内联,以启用跨源文件的内联(无论它们是否声明为
inline
,都由编译器决定什么是最好的)

inline
的要点是让编译器知道,如果它选择将函数内联到所有调用者中,则不需要发出函数的独立定义。(因为此函数的定义(而不仅仅是声明)将出现在使用它的所有翻译单元中。因此,如果其他文件决定不内联它,则可以在那里发出定义。)

现代编译器仍然使用常规的启发式方法来决定是否值得内联。例如,一个具有多个调用方的大型函数可能不会内联,以避免代码膨胀。
static
告诉编译器没有其他翻译单元可以看到该函数,因此如果该文件中只有一个调用方,它将非常喜欢我在那儿。(如果您有一个大函数,那么将其设置为
静态内联
,这是一个坏主意。您将在每个文件中获得一个定义副本,该副本位于它没有内联的位置,并且内联过于激进。对于可能到处内联的小函数,您可能仍然应该使用
内联
,而不是
静态内联
,因此在如果任何东西都取函数的地址,则所有文件中只共享一个定义。
inline
告诉链接器合并函数的重复定义,而不是出错。这种行为是
inline
真正做的事情中更重要的一部分,而不是对编译器的实际提示希望将其内联。)


gcc-fwhole程序(所有源文件都在同一命令行上)为编译器提供了足够的信息,使其能够自己做出所有这些决定。它可以查看函数在整个程序中是否只有一个调用方,并将其内联,而不是创建独立的定义、参数设置和
调用


gcc-flto
允许类似于整个程序的链接时间优化,但不需要命令行上的所有
.cpp
文件。相反,它将GIMPLE代码存储在
.o
文件中,并在链接时间完成优化。

编译器可以忽略
内联
指令,并且大部分情况下都会这样做在C++中使用<代码>内联的主要原因是指定链接,而不是告诉编译器要做什么。您需要GCC特定的代码> NoLyn> <代码>属性。.cp>内联在CPP文件中总是错误的。@ Sergaya胡说(您总是).cpp文件中的
内联
d函数有很多很好的理由:没有导出的函数(不是该文件中实现的接口的一部分),大多生活在匿名名称空间中。@Walter如果他们在匿名名称空间中,他们不需要标记为
inline
,因为他们只会定义一次。你说的“可靠”是什么意思?LTO存在,但非常脆弱,因为它不知道程序员打算在TUs.FWIW中安全使用哪些注释。示例中的
inline
是冗余的。inline成员定义是隐式内联的。
gcc -S -O0 main.cpp
    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 14
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    leaq    -8(%rbp), %rdi
    movl    $0, -4(%rbp)
    callq   __ZNK7myClass4getAEv
    xorl    %ecx, %ecx
    movl    %eax, -12(%rbp)         ## 4-byte Spill
    movl    %ecx, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function

.subsections_via_symbols
class myClass {
  …
  inline int getA() const { return a; }
  …
}
// file.h    
class myClass
{
    int a=1;
public:
    int getA() const { return a; }   // implicitly inline
    int getAlt() const;
};

// file.cc
#include "file.h"
int myClass::getAlt() const
{ return a; }

// main.cc
#include "file.h"
int main()
{
    myClass x;
    return x.getA() - x.getAlt();
}