C++ can compilers inline a function pointer?

后端 未结 4 1649
难免孤独
难免孤独 2020-12-02 18:31

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

template&l         


        
相关标签:
4条回答
  • 2020-12-02 19:17

    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++; };
       ...
    
    0 讨论(0)
  • 2020-12-02 19:18

    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.)

    0 讨论(0)
  • 2020-12-02 19:26

    Is the compiler able to inline the calls? Yes.

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

    0 讨论(0)
  • 2020-12-02 19:35

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

    0 讨论(0)
提交回复
热议问题