C++ (gcc)编译器是否优化了空体函数?

C++ (gcc)编译器是否优化了空体函数?,c++,gcc,optimization,C++,Gcc,Optimization,使用基于策略的设计,一种封装的算法: template< typename Policy> class EncapsulatedAlgorithm : public Policy { double x = 0; public: using Policy::subCalculate; void calculate() { Policy::subCalculate(x);

使用基于策略的设计,一种
封装的算法

template< typename Policy>
class EncapsulatedAlgorithm : public Policy
{
    double x = 0; 

    public: 
        using Policy::subCalculate; 

        void calculate()
        {
            Policy::subCalculate(x); 
        }
    protected:
        ~EncapsulatedAlgorithm() = default;
};
计算的人计算:

struct calculate
{
    static void subCalculate(double& x)
    {
        x = x * x; 
    }
};
有一个人把他们都带来,在黑暗中把他们捆绑起来:D-那是绝对没有用的:

以下是示例程序:

typedef EncapsulatedAlgorithm<doNothing> nothingDone; 
typedef EncapsulatedAlgorithm<calculate> calculationDone; 
typedef EncapsulatedAlgorithm<loggedCalculation>  calculationLogged; 

int main(int argc, const char *argv[])
{
    nothingDone n; 
    n.calculate(); 

    calculationDone c; 
    c.calculate();

    calculationLogged l; 
    l.calculate(); 

    return 0;
}
但我对汇编的了解还不够,无法确定地解释结果——结果文件很小,我无法识别函数调用,因为所有策略的静态函数的代码都是内联的

我能看到的是,当没有为设置优化时,在主函数中,有一个
调用
,以及一个与“doNothing::subCalculate”相关的后续
离开

call    _ZN9doNothing12subCalculateERd
leave
以下是我的问题:

  • 为了能够读懂
    g++-S
    吐出的东西,我从哪里开始学习
  • 空函数是否进行了优化?这些行在
    main.s
    中的什么位置
  • 这个设计可以吗。?通常,实现一个不做任何事情的函数是一件坏事,因为接口说的是完全不同的东西(
    subCalculate
    ,而不是
    doNothing
    ),但是在策略的情况下,策略名称清楚地表明该函数不会做任何事情。否则我需要做一些类型特征的事情,比如
    enable_if
    ,等等,只是为了排除单个函数调用 为了能够读懂g++-S所表达的内容,我从哪里开始学习

    本网站不推荐阅读材料。谷歌“x86汇编语言”

    空函数是否进行了优化?这些行在main.s中的什么位置

    它将在启用Optimizer时出现,因此生成的.S中不会有任何行。您已经在未优化的输出中找到调用

    事实上,即使是用于执行乘法的策略也可能被删除,因为编译器应该能够确定您没有使用结果值。添加代码以打印x的值,并从编译时无法知道的某个值中为x添加种子(在这样的小实验程序中使用argc通常很方便,然后您将强制编译器至少保留功能上重要的代码

    这个设计可以吗

    这取决于很多事情(例如,如果实现需要在头文件中公开,是否要使用模板,是否要为每个实例化处理不同的类型…),但是您正确地实现了设计

    通常,实现一个不做任何事情的函数是一件坏事,因为接口说的是完全不同的东西(subCalculate而不是doNothing),但在策略的情况下,策略名称清楚地表明函数不会执行任何操作。否则,我需要执行诸如enable_if等类型特征,以排除单个函数调用

    您可能需要仔细考虑函数名称…<代码> dothayii必需的计算()/<代码> <代码> EnSurReExpResiviVE()/<代码>而不是<代码> LoopyMutEXE()/代码>,<代码>后缀值>(代码)>而不是<代码> PrimtType < /代码>等。

    < p>我到了,显示程序集输出。
    template< typename Policy>
    struct EncapsulatedAlgorithm : public Policy
    {
            void calculate(double& x)
            {
                Policy::subCalculate(x); 
            }
    };
    struct doNothing
    {
        static void subCalculate(double& x)
        {
        }
    };
    void func(double& x) {
       EncapsulatedAlgorithm<doNothing> a;
       a.calculate(x);
    }
    

    嗯,我在那里的程序集中只看到两个操作码。
    rep
    (不知道那是什么)和结束函数。似乎G++编译器可以轻松地优化函数体。

    首先删除
    iostream
    ,以获得最小的输出。如果我这样做,并使用-O3编译示例,而不使用调试信息,它实际上会减少到
    返回0
    xorl%eax,%eax
    ,这只是将eax寄存器设置为
    0通过xoring对其自身进行xoring。问题中没有实质内容。如果编译器内联一个函数,并且该函数为空,则实际上没有什么需要进一步优化的,因此在该调用的其余优化阶段,No op将很好地完成。如果不允许内联,则调用将不会优化。因此,问题大致相等与此相关:编译器会做一些本应在beta测试中捕获并消除的极其愚蠢的事情吗?答案是“有时”:(您可以将中间Gimple转储与
    -fdump tree all
    …(它吐出数百个文件,但有些文件可读性很强,语法类似C)。另请参阅
    g++ -S -O3 -std=c++11 main.cpp
    
    call    _ZN9doNothing12subCalculateERd
    leave
    
    template< typename Policy>
    struct EncapsulatedAlgorithm : public Policy
    {
            void calculate(double& x)
            {
                Policy::subCalculate(x); 
            }
    };
    struct doNothing
    {
        static void subCalculate(double& x)
        {
        }
    };
    void func(double& x) {
       EncapsulatedAlgorithm<doNothing> a;
       a.calculate(x);
    }
    
                .Ltext0:
                    .globl  _Z4funcRd 
                _Z4funcRd:
                .LFB2:
                    .cfi_startproc    #void func(double& x) {
                .LVL0:
    0000 F3             rep           #not sure what this is
    0001 C3             ret           #}
                    .cfi_endproc
                .LFE2:
                .Letext0: