编译器去虚拟化,是不是太智能了?

问题描述 投票:0回答:1

我写了这个简短的程序来看看去虚拟化是如何工作的。编译器应该能够推断出正确的类型:

#include <iostream>
using std::cout;
using std::endl;

class Base 
{
public:
    void foo() { cout << "Base::foo" << endl; }
    virtual void bar() { cout << "Base::bar" << endl; }
    virtual ~Base() = default;
};

class Child : public Base 
{
public:
    void foo() { cout << "Child::foo" << endl; }
    void bar() { cout << "Child::bar" << endl; }
};

int main()
{
    Base* obj = new Child;
    obj->foo();
    obj->bar();
    delete obj;
}

通过

https://gcc.godbolt.org/
. 使用 gcc 5.3 和 clang 3.7 与 -O2 -std=c++11

编译。

结果是,两个编译器都无法优化所有内容 - gcc 内联

foo()
并对
bar()
进行虚拟调用,而 clang 调用
foo()
并对
bar()
进行去虚拟化和内联调用。

同时,如果我先调用

obj->bar();
,然后调用
obj->foo();
,编译器在优化方面没有问题 - clang 内联这两个调用,gcc 正常调用
bar()
,而不是虚拟调用并内联
foo()

任何人都可以解释这种行为吗?

c++ compiler-optimization devirtualization
1个回答
6
投票

这可能是因为编译器认为内联没有帮助,因为与函数调用的开销相比,

cout
太昂贵了。如果你用更简单的东西替换它,例如对成员的分配,它将被内联。请参阅下面的输出

#include <iostream>
using std::cout;
using std::endl;

class Base 
{
public:
    void foo() { i = 1; }
    virtual void bar() { i = 2; }
    virtual ~Base() = default;

    int i = 0;
};

class Child : public Base 
{
public:
    void foo() { i = 3; }
    void bar() { i = 4; }
};

int main()
{
    Base* obj = new Child;
    obj->foo();
    obj->bar();
    std::cout << obj->i << std::endl;
    //delete obj;
}

组装:

Base::bar():
        movl    $2, 8(%rdi)
        ret
Child::bar():
        movl    $4, 8(%rdi)
        ret
Base::~Base():
        ret
Child::~Child():
        ret
Child::~Child():
        jmp     operator delete(void*)
Base::~Base():
        jmp     operator delete(void*)
main:
        subq    $8, %rsp
        movl    $16, %edi
        call    operator new(unsigned long)
        movl    $4, %esi
        movl    std::cout, %edi
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        movq    %rax, %rdi
        call    std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
        xorl    %eax, %eax
        addq    $8, %rsp
        ret
        subq    $8, %rsp
        movl    std::__ioinit, %edi
        call    std::ios_base::Init::Init()
        movl    $__dso_handle, %edx
        movl    std::__ioinit, %esi
        movl    std::ios_base::Init::~Init(), %edi
        addq    $8, %rsp
        jmp     __cxa_atexit
© www.soinside.com 2019 - 2024. All rights reserved.