如果我声明一个大函数作为内联函数怎么办? [英] What if I declare a big function as inline function?

查看:147
本文介绍了如果我声明一个大函数作为内联函数怎么办?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我搜索了一些相关问题(例如 C ++中内联函数的优点) a>),但是我仍然有问题。



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


  1. 然后我可以将每个函数设置为内联函数吗?

  2. 如果我错误地将函数设置为








解决方案

仅使用 inline 才能满足单一定义规则。由于性能原因,不要打扰 inline


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


如果我们在谈论 inline 关键字,则 inline 是什么。

全部 inline 是确保函数不' t违反单一定义规则。它还可能为编译器提供一个提示,即编译器应该在内联中吹出代码,这可能提高或不提高速度,但编译器有权忽略该提示。事实上,在现代编译器中,他们将忽略这个提示很多时间。



编译器很可能不会内联代码具有大功能。这并不意味着函数不应该被声明为 inline ,然而 - 这一切都取决于函数如何声明,定义和使用。但是,它再次与性能无关。



在应用优化技术时,不要试图超越编译器。






让我们看看标准对所有这一切的看法。



7.1.2函数说明符




.5,9.3,11.3)与 inline 说明符
声明一个内联函数 inline 说明符指向
实现,在
调用点处的函数体的内联替换被优先于通常的函数调用机制。
在调用点处执行这个内联替换
不需要实现;但是,即使这个内联替换是
省略,7.1.2定义的内联函数的其他规则仍然应该遵守


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



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



因此,最终结果是,虽然7.1.2 / 2的原意可能是给程序员一个手动 - 优化技术,现代编译器优化如此积极,以至于这个原始的意图很大程度上无效。



所有剩下的 inline 是那些其他规则。那么那些其他规则是什么? (C ++ 11 verbiage)


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


让我们来看一个例子。假设我们有这个类 template



文件:foo.h



  #ifndef FOO_H 
#define FOO_H

#include< string>
#include< sstream>

class StringBuilder
{
public:
template< typename T>内联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

如果我们 #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 =<<一个;
std :: cout<< s<< \\\
;
}



输出:



  jdibling @ hurricane:〜/ dev / hacks $ ./hacks 
d = 3.14,a = 42

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

档案:other.cpp



  #include< iostream> 
#include< string>

#includefoo.h

void DoSomethingElse()
{
unsigned long l = -12345;
long l2 = 223344;

std :: string s = StringBuilder()
<< l =<< 1<< ,l2 =<< l2;
std :: cout<< s<< \\\
;
}



编译器输出:



  ninja:输入目录`。'
[1/3]构建CXX对象CMakeFiles / hacks.dir / main.o
[2/3]对象CMakeFiles / hacks.dir / other.o
[3/3]链接CXX可执行文件
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:在函数`std :: operator |(std :: _ Ios_Openmode,std :: _ Ios_Openmode)':
/ home / jdibling / dev / hacks / foo .h:21:多重定义`StringBuilder :: operator std :: string()const'
CMakeFiles / hacks.dir / main.o:/home/jdibling/dev/hacks/foo.h:21:首先定义这里
collect2:错误:ld返回1退出状态
ninja:build stopped:子命令失败。

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



我们可以通过使函数 inline 来修复这个问题:

  class StringBuilder 
{
public:
// [...]
inline操作符std :: string()const;
// ^^^^^^
private:
std :: stringstream mStream;
};



编译器输出:



  ninja:输入目录`。'
[1/3]构建CXX对象CMakeFiles / hacks.dir / main.o
[2/3]构建CXX对象CMakeFiles / hacks。 dir / other.o
[3/3]链接CXX可执行文件

现在运算符std :: string 在两个翻译单元上定义,具有完全相同的定义。它与在声明中直接定义函数的效果相同:

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


I search some related questions (such as Benefits of inline functions in C++?), but I still have questions.

If inline function is just to "provide a simple mechanism for the compiler to apply more OPTIMIZATIONS."

  1. Then can I set every function as inline function?
  2. If I wrongfully set a function as inline function, what would happen concerning performance?
  3. Any threshold that tells me what size of function should not be inline function?

解决方案

Use inline only to satisfy the One Definition Rule. Don't bother with inline for performance reasons.

If inline function is just to "provide a simple mechanism for the compiler to apply more OPTIMIZATIONS."

If we're talking about the inline keyword, then this is not what inline is about. Like, at all.

All inline does is ensure that a function doesn't violate the One Definition Rule. It might also provide a hint to the compiler that the compiler should blow the code out inline, which may or may not improve speed, but the compiler is entitled to ignore that hint. And in fact in modern compilers they will ignore this hint a great deal of the time.

One of the cases where the compiler is very likely to not blow the code out inline is with big functions. That doesn't mean the function shouldn't be declared inline, however -- it all depends on how the function is declared, defined and used. But, again, it has nothing to do with performance.

Don't try to outsmart the compiler when it comes to applying optimization techniques. It's far better at making your program fast than you are.


Let's see what the Standard has to say on all of this.

7.1.2 Function specifiers

2/A function declaration (8.3.5, 9.3, 11.3) with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by 7.1.2 shall still be respected.

This tells us that inline is a request to the compiler to blow the code out inline, and that compilers are not required to perform that substitution. But this also tells us that regardless of the compiler performing this inline substitution, the "other rules" defined in 7.1.2 still apply.

There was a time, long ago, when the optimization techniques employed by C++ compilers were primitive relative to the compilers of today. Back in those days, it might have made sense to use inline as an optimization technique. But these days, compilers are much better at optimizing your code. The compiler applies techniques that, today, would make your code faster than inlining ever would, even if the function isn't actually inlined. (One possible example, RVO.)

So the end result of this is that while the original intent of 7.1.2/2 might have been to give the programmer a manual-optimization technique, modern compilers optimize so aggressively that this original intent is largely moot.

So all that's left for inline are those "other rules." So what are those other rules? (C++11 verbiage)

4/An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (3.2). [ Note: A call to the inline function may be encountered before its definition appears in the translation unit. — end note ] If the definition of a function appears in a translation unit before its first declaration as inline, the program is ill-formed. If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required. An inline function with external linkage shall have the same address in all translation units. A static local variable in an extern inline function always refers to the same object. A string literal in the body of an extern inline function is the same object in different translation units. [ Note: A string literal appearing in a default argument is not in the body of an inline function merely because the expression is used in a function call from that inline function. — end note ] A type defined within the body of an extern inline function is the same type in every translation unit.

Let's take a look at an example. Suppose we have this class template:

File: 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     

If we #include this file in main.cpp and use the StringBuilder, everything's fine:

File: 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";
}

Output:

jdibling@hurricane:~/dev/hacks$ ./hacks 
d=3.14, a=42

But if we want to use the StringBuilder in a second translation unit, we're going to have a problem:

File: 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";
}

Compiler output:

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.

StringBuilder::operator std::string() is defined twice; once in main.cpp and again in other.cpp -- which violated the One Definition Rule.

We can fix this by making the function inline:

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

Compiler output:

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

This works because now operator std::string is defined on both translation units with exactly the same definition. It has the same effect as defining the function directly in the declaraton:

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

这篇关于如果我声明一个大函数作为内联函数怎么办?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆