2017-07-02 129 views
12

看看这段代码:编译器为什么不优化?

struct Data { 
}; 

struct Init { 
    Data *m_data; 

    Init() : m_data(new Data) { } 
    ~Init() { 
     delete m_data; 
    } 
}; 

class Object { 
    private: 
     const int m_initType; 
     Data *m_data; 
    public: 
     Object(const Init &init) : m_initType(0), m_data(init.m_data) { } 
     Object(Init &&init) : m_initType(1), m_data(init.m_data) { init.m_data = nullptr; } 
     ~Object() { 
      if (m_initType==1) { 
       delete m_data; 
      } 
     } 
}; 

void somefunction(const Object &object); // it is intentionally not defined 

void callInitA() { 
     Init x; 
     somefunction(x); 
} 

void callInitB() { 
     somefunction(Init()); 
} 

由于Object::m_initType是常量,它不构造后更改。因此,理论上,在callInitAcallInitB中,编译器在内联~Object()时知道m_initType的值。但是,gcc和叮当fails to apply这个优化,并且都检查了值m_initType

这是为什么?有没有针对这种优化的语言规则,或编译器是不是做这种优化?

(这个问题是密切相关的this,但它是一个更具体的问题,我希望我能得到这个答案)

+0

'Init g; void somefunction(Object object){object。〜Object();新(&对象)对象(g);}'也许? –

+4

这是一个小得多的例子,我认为它演示了同样的问题:https://godbolt.org/g/zTyctM - 如果你注释掉'somefunction',那么整个事情就会被优化,但是'somefunction'被称为编译器生成一个“不可能发生”的检查。 –

+2

MSVC++应用此优化。有些是,有些则不是,优化者并不是平等的。 –

回答

0

对象的析构函数是不是在你的例子内联,你有2个调用,其中在一个m_initType中是1而在另一个中是0.因此编译器必须支持这两个版本。 此外,我想你的实际代码比你的例子更复杂一点,所以编译器可能会认为内联整个析构函数代码比单独使用'if'的通用版本更昂贵。

+0

你可以在析构函数前写'inline'并用'g ++ -O3 -Winline -Werror'编译 - 我想你会发现它不会改变任何东西。 –

+1

我想你已经误解了我的问题。对象析构函数**是**内联的,因为它可以从反汇编中看出。当然,我的代码更复杂,但是这个缺少的优化确实发生在我发布的代码中。 – geza

+0

@JohnZwinck我无法猜测暗示了什么。我只回答无论我看到问题:)优化是自愿的,并不是由标准强制执行。 '内联'相同 - 你可以尽可能多地写,但根据标准,它只是建议。因此,如果编译器没有看到任何好处,可以自由地忽略“内联”。 – Noname

3

要回答是否有一个禁止这种优化的语言中的任何规则,这是我拿

[dcl.type.cv]

除任何类成员声明可变可以修改的,任何企图在其生命周期中修改一个const对象会导致未定义的行为。

所以理论上,优化器可以安全地假定m_initType在初始化后永远不会改变。这当然可以用来推断~Object中的分支是否会在编译时进行。

也就是说,只要观察到的行为保持不变,优化器可以自由地做任何事情,他们也可以自由忽略const。为了让优化器变得更复杂,混合中有一个前向声明但未定义的函数,优化器可能会在这之后放弃对该信息进行任何有用的操作。

Comparison of defined vs undefined function

如果函数定义以后,gcc和铛既优化了所有一切。但请注意,在这种特殊情况下,即使没有任何const,他们也会still do this

This post可能有兴趣

+1

感谢您的回答。是的,如果定义对编译器可见,它可以进行通常的优化。我编辑了我的问题,明确指出'somefunction'是故意没有定义的。 – geza

+0

如果'm_initType'不是一个对象,这个引用是不够的...(该标准有一个相当有限的对象定义) –