如果我将一个大函数声明为内联函数怎么办?

Dan*_*iel 1 c++ performance inline

我搜索了一些相关的问题(例如C++中的内联函数的好处?),但我仍有疑问.

如果内联函数只是"为编译器提供一个简单的机制来应用更多的OPTIMIZATIONS".

  1. 那么我可以将每个函数设置为内联函数吗?
  2. 如果我错误地将函数设置为内联函数,那么性能会发生什么?
  3. 任何告诉我函数大小不应该是内联函数的阈值?

Joh*_*ing 7

使用inline只满足一个定义规则.inline出于性能原因,请不要理会.

如果内联函数只是"为编译器提供一个简单的机制来应用更多的OPTIMIZATIONS".

如果我们谈论的是inline关键字,那么这不是什么意思inline.就像,完全一样.

所有这些inline 确保函数不违反单一定义规则.它还可能为编译器提供一个提示,即编译器应该将代码内联,这可能会或可能不会提高速度,但编译器有权忽略该提示.事实上,在现代编译器中,他们会在很多时候忽略这一暗示.

编译器很可能不会内联代码的情况之一是具有大功能.这并不意味着不应声明函数inline- 但这取决于函数的声明,定义和使用方式.但是,它再次与性能无关.

在应用优化技术时,不要试图超越编译器.它使你的程序比你快得多.


让我们看看标准对所有这些内容的评价.

7.1.2函数说明符

2 /带有inline说明符的函数声明(8.3.5,9.3,11.3)声明了一个内联函数.的inline说明符指示,在呼叫的点的功能体的内联取代是优选的,以通常的函数调用机制的实施.在呼叫点执行此内联替换不需要实现; 但是,即使省略了这种内联替换,仍应遵守7.1.2定义的内联函数的其他规则.

这告诉我们这inline是一个请求编译器将代码内联,并且编译器不需要执行该替换.但这也告诉我们,无论编译器执行此内联替换,7.1.2中定义的"其他规则"仍然适用.

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

因此,最终的结果是,虽然7.1.2/2的初衷可能是给程序员一个手动优化技术,但现代编译器如此积极地进行优化,以至于这个原始意图在很大程度上没有实际意义.

所以剩下的inline就是那些"其他规则".那么那些其他规则是什么?(C++ 11措辞)

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

我们来看一个例子吧.假设我们有这个类template:

文件:foo.h

#ifndef FOO_H     
#define FOO_H     

#include <string>     
#include <sstream>     

class StringBuilder    
{    
public:    
    template <typename T> inline StringBuilder& operator<<(const T& t)    
    {    
        mStream << t;    
        return * this;    
    }    
    operator std::string () const;    
private:    
    std::stringstream mStream;    
};    

StringBuilder::operator std::string() const     
{     
    return mStream.str();    
}     

#endif     
Run Code Online (Sandbox Code Playgroud)

如果我们#include这个文件main.cpp并使用StringBuilder,一切都很好:

文件:main.cpp

#include <iostream>
#include <string>

int main()
{
    double d = 3.14;
    unsigned a = 42; 

    std::string s = StringBuilder() 
        << "d=" << d << ", a=" << a;
    std::cout << s << "\n";
}
Run Code Online (Sandbox Code Playgroud)

输出:

jdibling@hurricane:~/dev/hacks$ ./hacks 
d=3.14, a=42
Run Code Online (Sandbox Code Playgroud)

但是如果我们想StringBuilder在第二个翻译单元中使用,我们就会遇到问题:

文件:other.cpp

#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";
}
Run Code Online (Sandbox Code Playgroud)

编译器输出:

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.
Run Code Online (Sandbox Code Playgroud)

StringBuilder::operator std::string()定义两次; 曾经一次main.cpp又一次other.cpp- 违反了"一个定义规则".

我们可以通过制作函数来解决这个问题inline:

class StringBuilder
{
public:
    // [...]
    inline operator std::string () const;
//  ^^^^^^
private:
    std::stringstream mStream;
};
Run Code Online (Sandbox Code Playgroud)

编译器输出:

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
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为现在operator std::string在两个具有完全相同定义的翻译单元上定义.它与在声明中直接定义函数具有相同的效果:

class StringBuilder
{
public:
    operator std::string () const
    {
        return mStream.str();
    }
private:
    std::stringstream mStream;
};
Run Code Online (Sandbox Code Playgroud)