C++ 如果我将一个大函数声明为内联函数呢?

C++ 如果我将一个大函数声明为内联函数呢?,c++,performance,inline,C++,Performance,Inline,我搜索了一些相关的问题(例如),但仍然有问题 如果内联函数只是“为编译器应用更多优化提供一个简单的机制” 那么我可以将每个函数都设置为内联函数吗 如果我错误地将一个函数设置为内联函数,那么在性能方面会发生什么 是否有任何阈值告诉我函数的大小不应该是内联函数 仅仅因为你让函数内联,并不意味着编译器必须让它内联。内联函数是对编译器的提示,而不是命令 编译器将使用一组度量标准,如优化级别、生成类型(调试或发布)、代码大小等来确定函数是否应内联。使用内联仅满足一个定义规则。出于性能原因,不要使用inli

我搜索了一些相关的问题(例如),但仍然有问题

如果内联函数只是“为编译器应用更多优化提供一个简单的机制”

  • 那么我可以将每个函数都设置为内联函数吗
  • 如果我错误地将一个函数设置为内联函数,那么在性能方面会发生什么
  • 是否有任何阈值告诉我函数的大小不应该是内联函数

  • 仅仅因为你让函数内联,并不意味着编译器必须让它内联。内联函数是对编译器的提示,而不是命令


    编译器将使用一组度量标准,如优化级别、生成类型(调试或发布)、代码大小等来确定函数是否应内联。

    使用
    内联
    仅满足一个定义规则。出于性能原因,不要使用
    inline

    如果内联函数只是为了“为 编译器应用更多优化。”

    如果我们谈论的是
    inline
    关键字,那么这不是
    inline
    的内容。一点也不

    inline
    所做的就是确保函数不违反一个定义规则。它还可能向编译器提供一个提示,即编译器应该以内联方式输出代码,这可能会提高速度,也可能不会提高速度,但编译器有权忽略该提示。事实上,在现代编译器中,他们会在很多时候忽略这一暗示

    编译器很可能不会将代码内联输出的一种情况是使用大函数。这并不意味着函数不应该声明为
    内联的
    ,但是,这完全取决于函数的声明、定义和使用方式。但是,同样,它与性能没有任何关系

    在应用优化技术时,不要试图智取编译器。它比你的程序快得多


    让我们看看标准对所有这些都有什么规定

    7.1.2函数说明符 2/A带有
    内联
    说明符的函数声明(8.3.5、9.3、11.3)
    声明一个内联函数。
    inline
    说明符指示 在 调用点是常用函数调用机制的首选。 执行此内联替换不需要实现 在呼叫点;但是,即使此内联替换是 省略,7.1.2中定义的内联函数的其他规则应 仍然值得尊重

    这告诉我们,
    inline
    是对编译器的一个请求,要求将代码以内联方式输出,编译器不需要执行这种替换。但这也告诉我们,无论编译器执行这种内联替换,7.1.2中定义的“其他规则”仍然适用

    有一段时间,很久以前,C++编译器使用的优化技术相对于今天的编译器来说是原始的。在那些日子里,使用
    inline
    作为一种优化技术可能是有意义的。但是现在,编译器在优化代码方面做得更好了。编译器应用了一些技术,这些技术可以使代码比内联更快,即使函数实际上没有内联。(一个可能的例子是RVO。)

    因此,最终的结果是,虽然7.1.2/2的初衷可能是给程序员一种手动优化技术,但现代编译器的优化如此之激进,以至于这个初衷在很大程度上没有意义

    所以,内联所剩下的就是那些“其他规则”。那么,那些其他规则是什么呢?(C++11逐字)

    4/应在每个翻译单元中定义内联函数 它是odr使用的,并且在 每例(3.2)。[注意:对内联函数的调用可能是 在其定义出现在翻译单元中之前遇到- 结束注释]如果函数的定义出现在翻译中 在第一次声明为内联之前,程序是 格式不正确。如果具有外部链接的函数在中声明为内联 一个翻译单元,应在所有翻译中声明为内联 其出现的单位;无需诊断。内联 具有外部链接的功能应在所有位置具有相同的地址 翻译单位。外部内联中的静态局部变量 函数总是引用同一个对象。字符串中的字符串文字 extern内联函数的主体是不同类型中的相同对象 翻译单位。[注意:默认值中出现的字符串文字 参数不在内联函数体中,这仅仅是因为 表达式用于来自该内联函数的函数调用。-结束 注意]外部内联函数体中定义的类型为 在每个翻译单元中使用相同的类型

    让我们看一个例子。假设我们有这个类
    模板

    文件:foo.h 输出: 但是如果我们想在第二个翻译单元中使用
    StringBuilder
    ,我们会遇到一个问题:

    文件:other.cpp
    StringBuilder::operator std::string()
    定义了两次;一次在
    main.cpp
    中,另一次在
    other.cpp
    中——这违反了一个定义规则

    我们可以通过使函数内联来解决这个问题:

    class StringBuilder
    {
    public:
        // [...]
        inline operator std::string () const;
    //  ^^^^^^
    private:
        std::stringstream mStream;
    };
    
    编译器输出: 这是因为现在
    运算符std::string
    在两个翻译单元上定义的定义完全相同。它与直接在声明中定义函数具有相同的效果:

    class StringBuilder
    {
    public:
        operator std::string () const
        {
            return mStream.str();
        }
    private:
        std::stringstream mStream;
    };
    

    如果我们假设编译器将在调用函数的位置粘贴内联代码,那么就会有大量重复代码

    功能
    jdibling@hurricane:~/dev/hacks$ ./hacks 
    d=3.14, a=42
    
    #include <iostream>
    #include <string>
    
    #include "foo.h"
    
    void DoSomethingElse()
    {
        unsigned long l = -12345;
        long l2 = 223344;
    
        std::string s = StringBuilder()
            << "l=" << l << ", l2=" << l2; 
        std::cout << s << "\n";
    }
    
    ninja: Entering directory `.'
    [1/3] Building CXX object CMakeFiles/hacks.dir/main.o
    [2/3] Building CXX object CMakeFiles/hacks.dir/other.o
    [3/3] Linking CXX executable hacks
    FAILED: : && /usr/bin/g++   -Wall -std=c++11 -g   CMakeFiles/hacks.dir/main.o CMakeFiles/hacks.dir/other.o  -o hacks  -rdynamic -lboost_regex-mt && :
    CMakeFiles/hacks.dir/other.o: In function `std::operator|(std::_Ios_Openmode, std::_Ios_Openmode)':
    /home/jdibling/dev/hacks/foo.h:21: multiple definition of `StringBuilder::operator std::string() const'
    CMakeFiles/hacks.dir/main.o:/home/jdibling/dev/hacks/foo.h:21: first defined here
    collect2: error: ld returned 1 exit status
    ninja: build stopped: subcommand failed.
    
    class StringBuilder
    {
    public:
        // [...]
        inline operator std::string () const;
    //  ^^^^^^
    private:
        std::stringstream mStream;
    };
    
    ninja: Entering directory `.'
    [1/3] Building CXX object CMakeFiles/hacks.dir/main.o
    [2/3] Building CXX object CMakeFiles/hacks.dir/other.o
    [3/3] Linking CXX executable hacks
    
    class StringBuilder
    {
    public:
        operator std::string () const
        {
            return mStream.str();
        }
    private:
        std::stringstream mStream;
    };