开发者

C++ can compilers inline a function pointer?

Suppose I've got a function functionProxy that takes a generic parameter function and call its operator():

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

The object passed to it may be开发者_JS百科:

  • a functor:

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

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

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

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

Will the compiler be able to inline function within functionProxy in all the above cases?


Sure thing.

It knows the value of function is the same as the value it passes it, knows the definition of the function, so just replaces the definition inline and calls the function directly.

I can't think of a condition where a compiler won't inline a one-line function call, it's just replacing a function call with a function call, no possible loss.


Given this code:

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

With MANUALLY_INLINE defined, we get this:

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  

And without, this:

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

The same. (Compiled with MSVC 2010, vanilla Release.)


Possibly. There is no strong reason for or against it, it just depends on what the compiler writers implemented.


Have tried the following templated pointer-to-lambda code:

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, and clang++ 3.5.0 all inlined both widget.f() and (*lambda_)() calls using -O2. That is, a was incremented directly inside main() according to disassembled binaries.

Inlining was applied even with non-const lambda and lambda_ pointers (removing both const).

Ditto with a local variable and lambda capture:

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


Is the compiler able to inline the calls? Yes.

Will it? Maybe. Check after you know it matters.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜