什么时候我应该写关键字内联的函数/方法在c++ ?

在看到一些答案后,一些相关的问题:

在c++中,什么时候我不应该为函数/方法写关键字“内联”? 什么时候编译器不知道什么时候使一个函数/方法'内联'? 当一个应用程序为一个函数/方法写“内联”时,它是否重要?


当前回答

在进行模板特化时,仍然需要显式内联函数(如果特化在.h文件中)

其他回答

在开发和调试代码时,不要使用内联。这会使调试复杂化。

添加它们的主要原因是为了帮助优化生成的代码。通常,这是以增加的代码空间换取速度,但有时内联可以同时节省代码空间和执行时间。

在算法完成之前将这种思想扩展到性能优化是不成熟的优化。

F.5:如果一个函数非常小并且对时间要求很高,那么就内联声明它

原因:一些优化器在没有程序员提示的情况下很擅长内联,但不要依赖它。测量!在过去40年左右的时间里,我们一直被承诺在没有人类提示的情况下,编译器可以比人类更好地内联。我们还在等待。指定inline(在类定义中编写成员函数时显式或隐式)可以鼓励编译器更好地完成工作。

来源:https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html Rf-inline

有关示例和异常,请访问源代码(参见上面)。

我想用一个令人信服的例子来解释这篇文章中所有的伟大答案,以消除任何剩余的误解。

给定两个源文件,例如:

inline111.cpp: # include < iostream > 空白栏(); Inline fun() { 返回111; } Int main() { std:: cout < <“inline111:有趣 () = " << 有趣的 () << ", & 有趣= " < < (void *)与娱乐; 酒吧(); } inline222.cpp: # include < iostream > Inline fun() { 返回222; } 空格条(){ std:: cout < <“inline222:有趣 () = " << 有趣的 () << ", & 有趣= " < < (void *)与娱乐; }


Case A: Compile: g++ -std=c++11 inline111.cpp inline222.cpp Output: inline111: fun() = 111, &fun = 0x4029a0 inline222: fun() = 111, &fun = 0x4029a0 Discussion: Even thou you ought to have identical definitions of your inline functions, C++ compiler does not flag it if that is not the case (actually, due to separate compilation it has no ways to check it). It is your own duty to ensure this! Linker does not complain about One Definition Rule, as fun() is declared as inline. However, because inline111.cpp is the first translation unit (which actually calls fun()) processed by compiler, the compiler instantiates fun() upon its first call-encounter in inline111.cpp. If compiler decides not to expand fun() upon its call from anywhere else in your program (e.g. from inline222.cpp), the call to fun() will always be linked to its instance produced from inline111.cpp (the call to fun() inside inline222.cpp may also produce an instance in that translation unit, but it will remain unlinked). Indeed, that is evident from the identical &fun = 0x4029a0 print-outs. Finally, despite the inline suggestion to the compiler to actually expand the one-liner fun(), it ignores your suggestion completely, which is clear because fun() = 111 in both of the lines.


Case B: Compile (notice reverse order): g++ -std=c++11 inline222.cpp inline111.cpp Output: inline111: fun() = 222, &fun = 0x402980 inline222: fun() = 222, &fun = 0x402980 Discussion: This case asserts what have been discussed in Case A. Notice an important point, that if you comment out the actual call to fun() in inline222.cpp (e.g. comment out cout-statement in inline222.cpp completely) then, despite the compilation order of your translation units, fun() will be instantiated upon it's first call encounter in inline111.cpp, resulting in print-out for Case B as inline111: fun() = 111, &fun = 0x402980.


Case C: Compile (notice -O2): g++ -std=c++11 -O2 inline222.cpp inline111.cpp or g++ -std=c++11 -O2 inline111.cpp inline222.cpp Output: inline111: fun() = 111, &fun = 0x402900 inline222: fun() = 222, &fun = 0x402900 Discussion: As is described here, -O2 optimization encourages compiler to actually expand the functions that can be inlined (Notice also that -fno-inline is default without optimization options). As is evident from the outprint here, the fun() has actually been inline expanded (according to its definition in that particular translation unit), resulting in two different fun() print-outs. Despite this, there is still only one globally linked instance of fun() (as required by the standard), as is evident from identical &fun print-out.

编译时,GCC默认不内联任何函数 启用优化。我不知道visual studio - deft_code

我检查了Visual Studio 9(15.00.30729.01)通过/FAcs编译并查看汇编代码: 编译器产生了对成员函数的调用,而没有在调试模式下启用优化。即使函数被标记为__forceinline,也不会产生内联运行时代码。

一个用例可能发生在继承上。例如,如果以下所有情况都为真:

你有某个类的基类 基类需要是抽象的 基类除了析构函数之外没有纯虚方法 您不希望为基类创建CPP文件,因为这是徒劳的

然后你必须定义析构函数;否则,你会有一些未定义的引用链接错误。此外,你不仅要定义,还要用inline关键字定义析构函数;否则,您将有多个定义链接错误。

这可能发生在一些只包含静态方法或编写基异常类的辅助类上。

让我们举个例子:

Base.h:

class Base {
public:
    Base(SomeElementType someElement) noexcept : _someElement(std::move(someElement)) {}

    virtual ~Base() = 0;

protected:
    SomeElementType _someElement;
}

inline Base::~Base() = default;

Derived1.h:

#include "Base.h"

class Derived1 : public Base {
public:
    Derived1(SomeElementType someElement) noexcept : Base(std::move(someElement)) {}

    void DoSomething1() const;
}

Derived1.cpp:

#include "Derived1.h"

void Derived1::DoSomething1() const {
    // use _someElement 
}

Derived2.h:

#include "Base.h"

class Derived2 : public Base {
public:
    Derived2(SomeElementType someElement) noexcept : Base(std::move(someElement)) {}

    void DoSomething2() const;
}

Derived2.cpp:

#include "Derived2.h"

void Derived2::DoSomething2() const {
    // use _someElement 
}

通常,抽象类有一些纯虚方法,而不是构造函数或析构函数。因此,你不必分离基类的虚析构函数的声明和定义,你可以只写virtual ~ base () = default;关于类声明。然而,在我们的案例中,情况并非如此。

据我所知,MSVC允许你在类声明上写这样的东西:virtual ~Base() = 0{}。所以你不需要用内联关键字分离声明和定义。但它将只与MSVC编译器工作。

现实世界的例子:

BaseException.h:

#pragma once

#include <string>

class BaseException : public std::exception {
public:
    BaseException(std::string message) noexcept : message(std::move(message)) {}
    virtual char const* what() const noexcept { return message.c_str(); }

    virtual ~BaseException() = 0;

private:
    std::string message;
};

inline BaseException::~BaseException() = default;

SomeException.h:

#pragma once

#include "BaseException.h"

class SomeException : public BaseException {
public:
    SomeException(std::string message) noexcept : BaseException(std::move(message)) {}
};

SomeOtherException.h:

#pragma once

#include "BaseException.h"

class SomeOtherException : public BaseException {
public:
    SomeOtherException(std::string message) noexcept : BaseException(std::move(message)) {}
};

main.cpp:

#include <SomeException.h>
#include <SomeOtherException.h>

#include <iostream>

using namespace std;

static int DoSomething(int argc) {
    try {
        switch (argc) {
        case 0:
            throw SomeException("some");
        case 1:
            throw SomeOtherException("some other");
        default:
            return 0;
        }
    }
    catch (const exception& ex) {
        cout << ex.what() << endl;
        return 1;
    }
}

int main(int argc, char**) {
    return DoSomething(argc);
}