2014-09-11 19 views
9

Tôi đang sử dụng từ khóa MS cụ thể để buộc một hàm toàn cục được gạch chân, nhưng tôi nhận thấy rằng hàm không tự nội tuyến nếu nó sử dụng một đối tượng có trình phá hủy tầm thường rõ ràng.Tìm hiểu hàm C++ Inlining

Trích dẫn từ MSDN

Ngay cả với __forceinline, trình biên dịch không thể inline mã trong tất cả hoàn cảnh. Trình biên dịch không thể nội tuyến một hàm nếu:

  • Hàm hoặc người gọi của nó được biên dịch với /Ob0 (tùy chọn mặc định để gỡ lỗi).

  • Chức năng và người gọi sử dụng các loại xử lý ngoại lệ khác nhau (xử lý ngoại lệ C++ trong một, xử lý ngoại lệ có cấu trúc trong trường còn lại).

  • Hàm có danh sách đối số biến.

  • Hàm sử dụng lắp ráp nội tuyến, trừ khi được biên dịch với /Og, /Ox, /O1 hoặc /O2.

  • Hàm này đệ quy và không kèm theo #pragma inline_recursion(on). Với pragma, các hàm đệ quy được đưa vào chiều sâu mặc định của 16 cuộc gọi. Để giảm độ sâu nội tuyến, hãy sử dụng inline_depth pragma.

  • Chức năng này là ảo và được gọi là hầu như. Các cuộc gọi trực tiếp đến các chức năng ảo có thể được nội tuyến.

  • Chương trình lấy địa chỉ của hàm và cuộc gọi được thực hiện qua con trỏ tới hàm. Các cuộc gọi trực tiếp đến các chức năng có địa chỉ được thực hiện có thể được inlined.

  • Chức năng này cũng được đánh dấu bằng công cụ sửa đổi __declspec trần truồng.

tôi đang cố gắng khép kín chương trình sau đây để kiểm tra hành vi

#include <iostream> 
#define INLINE __forceinline 
template <class T> 
struct rvalue 
{ 
    T& r_; 
    explicit INLINE rvalue(T& r) : r_(r) {} 
}; 

template <class T> 
INLINE 
T movz(T& t) 
{ 
    return T(rvalue<T>(t)); 
} 
template <class T> 
class Spam 
{ 
public: 
    INLINE operator rvalue<Spam>() { return rvalue<Spam>(*this); } 
    INLINE Spam() : m_value(0) {} 
    INLINE Spam(rvalue<Spam> p) : m_value(p.r_.m_value) {} 
    INLINE Spam& operator= (rvalue<Spam> p) 
    { 
     m_value = p.r_.m_value; 
     return *this; 
    } 
    INLINE explicit Spam(T value) : m_value(value) { } 
    INLINE operator T() { return m_value; }; 
    template <class U, class E> INLINE Spam& operator= (Spam<U> u) { return *this; } 
    INLINE ~Spam() {} 
private: 
    Spam(Spam<T>&); // not defined 
    Spam& operator= (Spam&); // not defined 
private: 
    T m_value; 
}; 
INLINE int foo() 
{ 
    Spam<int> p1(int(5)), p2; 
    p2 = movz(p1); 
    return p2; 
} 

int main() 
{ 
    std::cout << foo() << std::endl; 
} 

Với destructor tầm thường INLINE ~Spam() {} tại chỗ, chúng tôi đã sau tháo

int main() 
{ 
000000013F4B1010 sub   rsp,28h 
    std::cout << foo() << std::endl; 
000000013F4B1014 lea   rdx,[rsp+30h] 
000000013F4B1019 lea   rcx,[rsp+38h] 
000000013F4B101E mov   dword ptr [rsp+30h],5 
000000013F4B1026 call  movz<Spam<int> > (013F4B1000h) 
000000013F4B102B mov   rcx,qword ptr [__imp_std::cout (013F4B2050h)] 
000000013F4B1032 mov   edx,dword ptr [rax] 
000000013F4B1034 call  qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013F4B2040h)] 
000000013F4B103A mov   rdx,qword ptr [__imp_std::endl (013F4B2048h)] 
000000013F4B1041 mov   rcx,rax 
000000013F4B1044 call  qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013F4B2058h)] 
} 

nơi như mà không có destructor INLINE ~Spam() {} chúng tôi có sau tháo gỡ

int main() 
{ 
000000013FF01000 sub   rsp,28h 
    std::cout << foo() << std::endl; 
000000013FF01004 mov   rcx,qword ptr [__imp_std::cout (013FF02050h)] 
000000013FF0100B mov   edx,5 
000000013FF01010 call  qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013FF02040h)] 
000000013FF01016 mov   rdx,qword ptr [__imp_std::endl (013FF02048h)] 
000000013FF0101D mov   rcx,rax 
000000013FF01020 call  qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013FF02058h)] 
} 
000000013FF01026 xor   eax,eax 
} 

Tôi không hiểu, tại sao trong sự hiện diện của các destructor, trình biên dịch không nội tuyến chức năng T movz(T& t)

  • Note Các hành vi phù hợp 2008-2013
  • Note Tôi đã kiểm tra với Cygwin-gcc nhưng trình biên dịch không inline mã.Tôi không thể xác minh trình biên dịch khác tại thời điểm này, nhưng sẽ cập nhật trong 12 giờ tới nếu được yêu cầu
+0

Hỏi những người đã viết trình biên dịch và có thể gửi báo cáo lỗi. –

+0

@ n.m .: Tôi muốn, nhưng trước đó tôi muốn hỏi cộng đồng, nếu họ biết vấn đề này, hoặc nếu tôi thiếu điều gì đó hiển nhiên :-) – Abhijit

+1

Có thiếu '# define' nào không? 'INLINE __forceinline' – dyp

Trả lời

1

Vâng, đó là lỗi. Tôi đã thử nghiệm nó trên Qt trên môi trường trình biên dịch MinGW. Nó tối ưu mọi thứ rất tốt.

Trước tiên, tôi đã thay đổi mã của bạn một chút như sau cho dễ dàng hơn xem mã lắp ráp:

int main() 
{ 
    int i = foo(); 
    std::cout << i << std::endl; 
} 

Và từ tôi Qt của debug tháo:

 45 int main() 
     46 { 
0x401600     lea 0x4(%esp),%ecx 
0x401604 <+0x0004>   and $0xfffffff0,%esp 
0x401607 <+0x0007>   pushl -0x4(%ecx) 
0x40160a <+0x000a>   push %ebp 
0x40160b <+0x000b>   mov %esp,%ebp 
0x40160d <+0x000d>   push %ecx 
0x40160e <+0x000e>   sub $0x54,%esp 
0x401611 <+0x0011>   call 0x402160 <__main> 
0x401616 <+0x0016>   movl $0x5,-0x10(%ebp) 
     47  int i = foo(); 
0x401683 <+0x0083>   mov %eax,-0xc(%ebp) 
     48  std::cout << i << std::endl; 
0x401686 <+0x0086>   mov -0xc(%ebp),%eax 
0x401689 <+0x0089>   mov %eax,(%esp) 
0x40168c <+0x008c>   mov $0x6fcba2c0,%ecx 
0x401691 <+0x0091>   call 0x401714 <_ZNSolsEi> 
0x401696 <+0x0096>   sub $0x4,%esp 
0x401699 <+0x0099>   movl $0x40171c,(%esp) 
0x4016a0 <+0x00a0>   mov %eax,%ecx 
0x4016a2 <+0x00a2>   call 0x401724 <_ZNSolsEPFRSoS_E> 
0x4016a7 <+0x00a7>   sub $0x4,%esp 
     49 } 
0x4016aa <+0x00aa>   mov $0x0,%eax 
0x4016af <+0x00af>   mov -0x4(%ebp),%ecx 
0x4016b2 <+0x00b2>   leave 
0x4016b3 <+0x00b3>   lea -0x4(%ecx),%esp 
0x4016b6 <+0x00b6>   ret 

Bạn thậm chí có thể nhìn thấy foo() được tối ưu hóa. Bạn có thể thấy biến 'i' được gán trực tiếp cho 5 và được in.