【问题标题】:C++ can compilers inline a function pointer?C++ 编译器可以内联函数指针吗?
【发布时间】:2011-06-19 03:31:34
【问题描述】:

假设我有一个函数functionProxy,它接受一个泛型参数function 并调用它的operator()

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;
    }
    
  • 一个 (C++0x) lambda 函数:

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

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

编译器能否在上述所有情况下将function 内联到functionProxy 中?

【问题讨论】:

    标签: c++ function optimization compiler-construction inline


    【解决方案1】:

    当然。

    它知道function的值和它传给它的值是一样的,知道函数的定义,所以直接替换定义inline,直接调用函数。

    我想不出编译器不会内联单行函数调用的情况,它只是用函数调用替换函数调用,没有可能的损失。


    鉴于此代码:

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

    定义了MANUALLY_INLINE,我们得到这个:

    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  
    

    没有,这个:

    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
    

    同样的。 (与 MSVC 2010 一起编译,香草版本。)

    【讨论】:

    • 原则上听起来不错,但它是否通过了尝试和查看测试?不幸的是,我现在/这些天太忙了,无法检查。很好奇,因为这发生在我身上。
    • @Potatoswatter:为您的观赏乐趣而更新。
    • 嗯,不错! GCC 也会生成相同的代码(当优化开启时)。
    • @GMan:我想(除非 LTO 启动)必须内联定义函数(而不是在另一个翻译单元中)?
    • @Matt:这听起来对我来说是正确的。我想我不仅测试了调用是否是内联的,还测试了函数。我会测试你是否愿意,但我怀疑如果没有内联被调用函数的能力,它会尽其所能并内联调用本身。
    【解决方案2】:

    可能。没有强烈的理由支持或反对它,它只取决于编译器编写者实现了什么。

    【讨论】:

      【解决方案3】:

      已尝试以下模板化的指向 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 和 clang++ 3.5.0 都使用 -O2 内联了 widget.f()(*lambda_)() 调用。也就是说,a 根据反汇编的二进制文件直接在 main() 内部递增。

      即使使用非 const lambdalambda_ 指针也应用了内联(同时删除了 const)。

      同上,使用局部变量和 lambda 捕获:

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

      【讨论】:

        【解决方案4】:

        编译器能够内联调用吗?是的。

        会吗?可能是。检查你know it matters

        【讨论】:

          猜你喜欢
          • 2011-10-13
          • 1970-01-01
          • 2012-01-17
          • 2012-02-11
          • 2013-11-07
          • 1970-01-01
          • 1970-01-01
          • 2013-11-22
          相关资源
          最近更新 更多