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

2024-05-08

我搜索了一些相关问题(例如C++ 中内联函数的好处? https://stackoverflow.com/questions/145838/benefits-of-inline-functions-in-c),但我还有疑问。

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

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

Use inline只是为了满足单一定义规则。别打扰inline出于性能原因。

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

如果我们谈论的是inline关键字,那么这是not what inline是关于。就像,完全一样。

All inline does确保函数不违反单一定义规则。它还可能向编译器提供提示,提示编译器应该内联吹出代码,这可能会也可能不会提高速度,但编译器有权忽略该提示。事实上,在现代编译器中,他们很多时候都会忽略这个提示。

编译器很可能不会内联吹出代码的情况之一是使用大函数。这并不意味着不应声明该函数inline然而,这完全取决于函数的声明、定义和使用方式。但话又说回来,它已经nothing与性能有关。

在应用优化技术时,不要试图超越编译器。它在使你的程序运行得更快方面比你要好得多。


让我们看看标准对这一切有何规定。

7.1.2 函数说明符

2/A 函数声明(8.3.5、9.3、11.3)inline说明符 声明一个内联函数. The inline说明符指示 实现函数体的内联替换 调用点优于通常的函数调用机制。 执行此内联替换不需要实现 在通话时;然而,即使这个内联替换是 省略,7.1.2定义的内联函数的其他规则应 仍然受到尊重。

这告诉我们inline是向编译器发出内联代码吹出的请求,并且编译器不需要执行该替换。但这也告诉我们,无论编译器执行此内联替换,7.1.2 中定义的“其他规则”仍然适用。

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

因此,最终结果是,虽然 7.1.2/2 的最初意图可能是为程序员提供手动优化技术,但现代编译器的优化过于激进,以至于这一最初意图在很大程度上没有实际意义。

所以剩下的一切inline那些“其他规则”。那么其他规则是什么? (C++11 的用语)

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

让我们看一个例子。假设我们有这个类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     

If we #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";
}

Output:

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

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

文件:其他.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";
}

编译器输出:

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()被定义两次;曾在main.cpp并再次在other.cpp——这违反了单一定义规则。

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

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

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

class StringBuilder
{
public:
    operator std::string () const
    {
        return mStream.str();
    }
private:
    std::stringstream mStream;
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果我将一个大函数声明为内联函数怎么办? 的相关文章

随机推荐

  • 从 Kotlin 中的字符串中删除字符

    我正在尝试创建一个使用 Kotlin 中的字符串的 Android 计算器 如果逗号 或负数 已经包含一个 我不知道如何删除它 这是我的代码 它正确添加逗号 但如果用户再次单击则不会删除它 if buClickValue contains
  • 如何从 pandas groupby 中的多列中获取唯一值

    从这个数据框 df 开始 df pd DataFrame c 1 1 1 2 2 2 l1 a a b c c b l2 b d d f e f c l1 l2 0 1 a b 1 1 a d 2 1 b d 3 2 c f 4 2 c e
  • getAnnotations() 为空

    我想在我的应用程序中使用注释 因此 我为注释创建了 hello world 如下示例 public class HelloAnnotation Foo bar Hello World public String str public sta
  • R2WinBUGS - 使用模拟数据进行逻辑回归

    我只是想知道是否有人有一些使用 R2WinBUGS 包来运行逻辑回归的 R 代码 理想情况下使用模拟数据来生成 真相 和两个连续协变量 Thanks 基督教 PS 生成人工数据 一维情况 并通过 r2winbugs 运行 winbugs 的
  • 如何让 Casper JS 返回指示测试成功状态的退出代码?

    我希望能够进行一组 Casper JS 测试 并在成功时获得返回 0 的退出代码 在错误或测试失败时返回非零的退出代码 我想从 java 运行 casper 命令并确定测试是否通过 我遇到的问题是总是返回退出代码 0 这是发生这种情况的示例
  • “struct X typedef”与“typedef struct X”的含义是什么?

    我在现有代码库中有以下 工作 代码 在 C 和 C 之间共享的包含文件中使用 在 MSVC 2010 和 Windows DDK 上编译 struct X USHORT x typedef X PX And enum MY ENUM enu
  • Android 中未找到 PhoneGap 类错误

    我的 PhoneGap Android 应用程序遇到一些问题 到目前为止我明白了 我已经把一切都做好了 这是我所做的 在 Eclipse 中创建项目后 我在 libs 文件夹中添加了 cordova 2 2 0 jar 然后我编辑了Andr
  • bitbucket、“hg 推送”和“hg 更新”

    如果我从本地 Mercurial 存储库开始 我认为它是 主要 存储库 请原谅我的 dvcs 领主 并打算使用 bitbucket 作为备份和问题跟踪工具 我可以在本地进行所有更改repo 并执行 hg Push 将更改发送回 bitbuc
  • 循环放入和取出剪贴板,无延迟

    我正在使用以下代码将文本复制到剪贴板 System Windows Forms SendKeys SendWait c 然后我用 Clipboard GetText 从剪贴板获取文本 它工作正常 但当我循环使用剪贴板并且我得到的内容应该被下
  • 持续集成的投资回报率是多少?

    目前 我们的组织没有实行持续集成 为了让我们启动并运行 CI 服务器 我需要生成一份文档来证明投资回报 除了通过尽早发现和修复错误来节省成本之外 我很好奇我可以将其写入本文档的其他好处 节省 我喜欢 CI 的第一个原因是它有助于防止开发人员
  • 使用 Morphia 配置 Spring Boot?

    我不想利用 Spring DATA MongoDB 支持 我想利用名为 Morphia 的 MongoDB ORM https github com mongodb morphia https github com mongodb morp
  • 如何在 Angular JS 中显示以字节数组形式接收的图像

    我有一个将返回图像的服务器端应用程序 这些是响应标头 Content Disposition attachment filename 8822a009 944e 43f4 999b d297198d302a 1 0 low res Cont
  • SqlCommand返回值参数

    也许查看此代码的其他人能够告诉我为什么 returnID 始终为 0 我正在尝试从插入的记录中检索新的 ID public int AddToInventory int PartID int QtyOnHand int SpokenFor
  • 使用新数据输入自动更新图表

    我的图表从 DataGridView 加载数据 如果将新值插入到 DataGridView 中 我希望自动使用新数据更新图表 我的图表必然是table1 and table2在我的 DataGridView 中 它从 DataTable 获
  • 从 Harp.js 中的 EJS 模板调用另一个文件上的 javascript 函数

    尝试使用 Harp js 制作一个网站 我使用 ejs 模板 并希望将一些有用的 javascript 函数存储在中央文件中 我怎么做 我尝试使用 但它不起作用 似乎js文件没有被解析 有任何想法吗 谢谢 尽管有多种方法 有时 可以实现这一
  • 如何使用 javascript 选择页面上的任意文本?

    假设我有一个 contentEditablediv 用户可以编辑和更改其中的文本和元素 我如何任意更改此选择div用JavaScript 我所说的 更改 并不是指 更改用户选择的内容 我的意思是实际上更改what被选中 然后 用户应该能够在
  • 如何用C语言测量时间?

    我想知道某个代码块执行了多长时间 大约 像这样的事情 startStopwatch do some calculations stopStopwatch printf lf timeMesuredInSeconds How 您可以使用clo
  • Android 中的 ImageView 拖动限制

    我在布局中有一个 ImageView 并在 ImageView 上设置 OnTouchListener 来拖动 ImageView 它工作得很好 我的问题是如何防止将 ImageView 移动到布局范围之外 这是我的代码 活动类别 publ
  • 如何远程调试长时间运行的 python 脚本或服务?

    正如标题所说 我希望能够连接到在 Paster 或 uwsgi 下运行的 python 进程并利用 pdb 功能 Using winpdb http winpdb org 您可以像这样附加到正在运行的进程 插入 import rpdb2 r
  • 如果我将一个大函数声明为内联函数怎么办?

    我搜索了一些相关问题 例如C 中内联函数的好处 https stackoverflow com questions 145838 benefits of inline functions in c 但我还有疑问 如果内联函数只是为了 为编译