不可内联的功能

时间:2013-11-12 23:21:32

标签: c++ inline

我必须显示无法在c ++中内联的函数。

要检查这一点,我设置了标记-Winline

使用递归函数,我能够生成一个无法内联的函数。

但我尝试使用继承和“虚拟”关键字。但是我不能让编译器抱怨内联不可能有内容。

我知道这个话题已经涵盖了很多。但我没有找到一个有效的例子。我的编译器“聪明”: - )

我试过这个:

class virt1
{
public:
    virt1(){};
    inline virtual int virtFunc(int a){ return a*a; };
    virtual ~virt1(){};
};

class virt2 : public virt1
{
public:
    virt2(){};
    inline virtual int virtFunc(int a){ return a+a;};
    virtual ~virt2(){};
};

void testVirtFunc(virt2 &obj)
{
     std::cout << obj.virtFunc(2);
}

2 个答案:

答案 0 :(得分:5)

没有“可以倾斜”或“不可倾斜”的概念。函数只有inlinable或non-inlinable calls 。声明inline的属性是函数本身的属性。无法使用(或不可倾斜)的属性是特定呼叫的属性。这是两个不同的概念。

你似乎在混合这两个完全不相关的概念。

可以声明每个函数inline。可以声明虚函数inline。递归函数也可以声明为inline。它没什么奇怪的。你的期望在某种程度上是非法的是完全没有根据的。声明函数inline始终是合法的。

同时,对这些函数的实际调用是否将被内联是一个完全不同的独立问题。对某个函数的一次调用可以是无限的,而对另一个函数的另一次调用可以是不可嵌入的。 “无法忍受”也是一种每次通话的财产。

确实无法内联动态分派的虚拟呼叫。同时,在智能编译器可以在编译时正确预测虚拟调用中使用的对象的动态类型的情况下,编译器可能会生成对虚函数的直接调用(不使用动态调度的调用)。这个电话很容易被内联。

例如,根据您的声明,此调用

virt1 *v = rand() % 2 ? new virt1() : new virt2();
v->virtFunc(5);

无法内联。但这些电话

virt2 v2;
v2.virtFunc(6);

virt1* v1 = &v2;
v1->virtFunc(7);

可以毫无问题地内联。

通过指向函数的指针进行的间接调用通常无法内联。但是如果编译器在编译时以某种方式知道指针的确切值,那么调用可以用直接调用替换并内联。

也可以内联递归函数调用(尽管你不这么认为)。它们可以内联到某个固定的递归深度。这就是大多数编译器内联递归函数的方式:它们使用深度限制内联来“解包”递归,就像它们打开循环一样。

答案 1 :(得分:2)

内联意味着稍微超过“注册”到编译器。 (因为编译器在弃用之前很久就完全忽略了register关键字,在某种意义上命令是相似的,它们不是,甚至不是)

回到那一天他们就像“我会珍惜用户的这些知识”,现在他们就像“感谢你对此事的意见”然后丢弃它。他们处于比我们判断的更好的位置。

许多工作已经用于减少抽象的惩罚。这意味着编译器可以通过一些比以前更复杂的语句推断出更好的类型,从而推迟了v-table跳转。如果它可以解决所谓的内容,它可能会决定它是否值得内联和类似的内容。

模板曾经暗示内联,如果你说内联编译器会让你像个小孩一样做卡片技巧(并且弄错了)。

在编译器能够确定您调用的方法的情况下,没有理由不能内联虚拟内容。就像它如何优化变量一样,它可以优化函数指针,而虚拟基本上隐藏了虚拟指针的结构。

如果我做了some_derived_instance.some_vritual_method(),那么回到过去的日子。没有理由不能内联。内联从未说过“它必须在内”,就像寄存器一样,只是给了编译器一个提示。

更直接地回答问题

您的代码不是CONVOLUTED足以混淆编译器。 -Winline在编译器最终实现调用时无法内联函数时发出警告。

你会发现很难让代码在一个代码文件中进行错综复杂而无法告诉你。别名分析意味着联盟不会隐藏它。编译器对代码有很好的理解,可以在一定程度上“运行代码”。

要真正测试它,请将类的代码放在一个文件中,然后在另一个文件中使用base clas。然后编译器在编译测试函数时无法知道发生了什么,因为调用站点在另一个目标文件中。

不要使用链接时间优化,编译仍然可能内联(使用GCC,它们只是流式传输GIMPLE IR,其中包含所有信息)

底线

试图愚弄现代编译器是一个婊子。

Re:递归

编译器不优化代码,它优化了一些中间表示。它可以“内联递归”,它表示一个递归函数,作为调用点上的代码片段。

没有递归限制它对它愿意创建的blob的大小有限制。如果你“调整”内联器是超级激进的,你可以让GCC产生巨大的输出。想想俄罗斯娃娃,但每个都有N个孩子。