2014-09-11 17 views
9

Sto utilizzando una parola chiave specifica MS per forzare una funzione globale inline, ma ho notato che la funzione non riesce ad essere in linea se utilizza un oggetto che ha un distruttore banale esplicito.Informazioni sulla funzione C++ Inlining

Citando MSDN

Anche con __forceinline, il compilatore non può codice inline in tutte le circostanze . Il compilatore non può inline una funzione se:

  • La funzione o il suo chiamante è compilato con /Ob0 (l'opzione predefinita per build di debug).

  • La funzione e il chiamante utilizzano diversi tipi di gestione delle eccezioni (gestione delle eccezioni C++ in una gestione delle eccezioni strutturata nell'altro).

  • La funzione ha un elenco di argomenti variabili.

  • La funzione utilizza assembly inline, a meno compilato con /Og, /Ox, /O1 o /O2.

  • La funzione è ricorsiva e non è corredata di #pragma inline_recursion(on). Con il pragma, le funzioni ricorsive sono integrate in una profondità predefinita di 16 chiamate. Per ridurre la profondità di allineamento, utilizzare il pragma inline_depth.

  • La funzione è virtuale e viene chiamata virtualmente. Le chiamate dirette alle funzioni virtuali possono essere sottolineate.

  • Il programma prende l'indirizzo della funzione e la chiamata viene effettuata tramite il puntatore alla funzione. Le chiamate dirette alle funzioni che hanno ricevuto il loro indirizzo possono essere sottolineate.

  • La funzione è contrassegnata anche con il modificatore __declspec nudo.

sto cercando il seguente programma autonomo per testare il comportamento

#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; 
} 

Con il distruttore banale INLINE ~Spam() {} a posto, abbiamo il seguente smontaggio

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)] 
} 

dove come senza il distruttore INLINE ~Spam() {} abbiamo il seguente smontaggio

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 
} 

io sto riuscendo a capire, perché in presenza del distruttore, il compilatore non riesce a inline la funzione T movz(T& t)

  • Nota Il comportamento è coerente 2008-2013
  • Nota Ho controllato con cygwin-gcc ma il compilatore non fa il codice.Non posso verificare altri compilatori in questo momento, ma aggiornerei nelle prossime 12 ore se necessario
+0

chiedere alle persone che hanno scritto il compilatore, e, eventualmente, un bug report. –

+0

@ N.m .: vorrei, ma prima voglio chiedere alla comunità, se sono a conoscenza di questo problema, o se mi manca qualcosa ovvio :-) – Abhijit

+1

Esiste un '# define' manca? 'INLINE __forceinline' – dyp

risposta

1

Sì, è un bug. L'ho provato su Qt rispetto all'ambiente del compilatore MinGW. Ottimizza tutto molto bene.

In primo luogo, ho cambiato il codice un po 'come sotto per facilitare la visualizzazione del codice assembly:

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

E dal mio di debug lo smontaggio di Qt:

 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 

si può anche vedere che foo() è ottimizzato. Puoi vedere che la variabile 'i' è assegnata direttamente a 5 e stampata.