2011-02-01 9 views
24

Supponiamo che io ho una funzione di functionProxy che prende un parametro generico function e chiamare il operator():I compilatori C++ possono inline un puntatore a funzione?

template< typename Function > void functionProxy(Function function) { 
    function(); 
} 

L'oggetto passato ad esso può essere:

  • un funtore:

    struct Functor { 
        void operator()() const { 
         std::cout << "functor!" << std::endl; 
        } 
    }; 
    
  • una funzione: funzione

    void function() { 
        std::cout << "function!" << std::endl; 
    } 
    
  • un (C++ 0x) lambda:

    [](){ std::cout << "lambda!" << std::endl; } 
    

int main() 
{ 
    functionProxy(Functor()); 
    functionProxy(function); 
    functionProxy([](){ std::cout << "lambda!" << std::endl; }); 
    return 0; 
} 

Sarà il compilatore poter all'inline function all'interno functionProxy in tutti i casi precedenti?

risposta

26

Sicuro.

Conosce il valore di function uguale al valore che lo passa, conosce la definizione della funzione, quindi sostituisce semplicemente la definizione inline e chiama direttamente la funzione.

Non riesco a pensare a una condizione in cui un compilatore non eseguirà una chiamata di funzione su una linea, sostituirà semplicemente una chiamata di funzione con una chiamata di funzione, nessuna perdita possibile.


Dato questo codice:

#include <iostream> 

template <typename Function> 
void functionProxy(Function function) 
{ 
    function(); 
} 

struct Functor 
{ 
    void operator()() const 
    { 
     std::cout << "functor!" << std::endl; 
    } 
}; 

void function() 
{ 
    std::cout << "function!" << std::endl; 
} 

//#define MANUALLY_INLINE 

#ifdef MANUALLY_INLINE 
void test() 
{ 
    Functor()(); 

    function(); 

    [](){ std::cout << "lambda!" << std::endl; }(); 
} 
#else 
void test() 
{ 
    functionProxy(Functor()); 

    functionProxy(function); 

    functionProxy([](){ std::cout << "lambda!" << std::endl; }); 
} 
#endif 

int main() 
{ 
    test(); 
} 

Con MANUALLY_INLINE definito, otteniamo questo:

test: 
00401000 mov   eax,dword ptr [__imp_std::endl (402044h)] 
00401005 mov   ecx,dword ptr [__imp_std::cout (402058h)] 
0040100B push  eax 
0040100C push  offset string "functor!" (402114h) 
00401011 push  ecx 
00401012 call  std::operator<<<std::char_traits<char> > (401110h) 
00401017 add   esp,8 
0040101A mov   ecx,eax 
0040101C call  dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)] 
00401022 mov   edx,dword ptr [__imp_std::endl (402044h)] 
00401028 mov   eax,dword ptr [__imp_std::cout (402058h)] 
0040102D push  edx 
0040102E push  offset string "function!" (402120h) 
00401033 push  eax 
00401034 call  std::operator<<<std::char_traits<char> > (401110h) 
00401039 add   esp,8 
0040103C mov   ecx,eax 
0040103E call  dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)] 
00401044 mov   ecx,dword ptr [__imp_std::endl (402044h)] 
0040104A mov   edx,dword ptr [__imp_std::cout (402058h)] 
00401050 push  ecx 
00401051 push  offset string "lambda!" (40212Ch) 
00401056 push  edx 
00401057 call  std::operator<<<std::char_traits<char> > (401110h) 
0040105C add   esp,8 
0040105F mov   ecx,eax 
00401061 call  dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)] 
00401067 ret 

E senza, questo:

test: 
00401000 mov   eax,dword ptr [__imp_std::endl (402044h)] 
00401005 mov   ecx,dword ptr [__imp_std::cout (402058h)] 
0040100B push  eax 
0040100C push  offset string "functor!" (402114h) 
00401011 push  ecx 
00401012 call  std::operator<<<std::char_traits<char> > (401110h) 
00401017 add   esp,8 
0040101A mov   ecx,eax 
0040101C call  dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)] 
00401022 mov   edx,dword ptr [__imp_std::endl (402044h)] 
00401028 mov   eax,dword ptr [__imp_std::cout (402058h)] 
0040102D push  edx 
0040102E push  offset string "function!" (402120h) 
00401033 push  eax 
00401034 call  std::operator<<<std::char_traits<char> > (401110h) 
00401039 add   esp,8 
0040103C mov   ecx,eax 
0040103E call  dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)] 
00401044 mov   ecx,dword ptr [__imp_std::endl (402044h)] 
0040104A mov   edx,dword ptr [__imp_std::cout (402058h)] 
00401050 push  ecx 
00401051 push  offset string "lambda!" (40212Ch) 
00401056 push  edx 
00401057 call  std::operator<<<std::char_traits<char> > (401110h) 
0040105C add   esp,8 
0040105F mov   ecx,eax 
00401061 call  dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)] 
00401067 ret 

Lo stesso. (Compilato con MSVC 2010, rilascio vaniglia.)

+1

Suona bene in linea di principio, ma lo fa passare il provare e vedere di prova? Sfortunatamente sono troppo impegnato adesso/in questi giorni per controllare. Molto curioso anche se questo mi è venuto in mente. – Potatoswatter

+0

@Potatoswatter: eseguirò un test adesso, un momento. – GManNickG

+0

@Potatoswatter: aggiornato per il piacere della visualizzazione. – GManNickG

0

Possibilmente. Non c'è una forte ragione a favore o contro di esso, dipende solo da ciò che gli scrittori di compilatori implementarono.

-3

Il compilatore è in grado di incorporare le chiamate? ? Sì.

Sarà? Può essere. Verifica dopo il know it matters.

-2

Il Functor e il lambda saranno inline perché sono oggetti statless (tutte le informazioni sono disponibili in fase di compilazione).

Funzione puntatori e boost :: Oggetti funzione (ora in std: :) non può essere inlineato perché non è chiaro in fase di compilazione quale funzione puntano a. Se sono costanti, le cose possono variare.

0

hanno provato il seguente codice su modelli puntatore a lambda:

volatile static int a = 0; 

template <typename Lambda> class Widget { 
    public: 
     Widget(const Lambda* const lambda) : lambda_(lambda) { } 
     void f() { (*lambda_)(); } 
    private: 
     const Lambda* const lambda_; 
}; 

int main() { 
    auto lambda = [](){ a++; }; 
    Widget<decltype(lambda)> widget(&lambda); 
    widget.f(); 
} 

GNU g ++ 4.9.2, Intel ICPC 16.0.1, e clang ++ 3.5.0 tutti inline sia widget.f() e (*lambda_)() chiamate utilizzando -O2. Cioè, a è stato incrementato direttamente all'interno di main() in base ai binari smontati.

Inlining è stato applicato anche con puntatori non-const lambda e lambda_ (rimozione di entrambi const).

Idem con una cattura variabile e lambda locali:

int main() { 
    volatile int a = 0; 
    auto lambda = [&a](){ a++; }; 
    ... 
Problemi correlati