C ++ может компиляторы встроить указатель на функцию? - PullRequest
27 голосов
/ 01 февраля 2011

Предположим, у меня есть функция 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;
    }
    
  • a (C ++ 0x) лямбда-функция:

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

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

Сможет ли компилятор встроить function в functionProxy во всех вышеуказанных случаях?

Ответы [ 5 ]

29 голосов
/ 01 февраля 2011

Конечно.

Он знает, что значение function совпадает со значением, которое он передает ему, знает определение функции, поэтому просто заменяет определение в строке и вызывает функцию напрямую.

Я не могу представить себе условие, когда компилятор не встроит вызов функции в одну строку, он просто заменяет вызов функции вызовом функции, без возможных потерь.


Учитывая этот код:

#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, версия vanilla.)

0 голосов
/ 04 декабря 2015

Пробовали следующий шаблонный указатель на лямбда-код :

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, все встроенные обаwidget.f() и (*lambda_)() звонки с использованием -O2.Таким образом, a был увеличен непосредственно внутри main() в соответствии с разобранными двоичными файлами.

Подстановка была применена даже с неконстантными указателями lambda и lambda_ (удаляя оба const).

То же самое с локальной переменной и лямбда-захватом:

int main() {
   volatile int a = 0;
   auto lambda = [&a](){ a++; };
   ...
0 голосов
/ 02 февраля 2011

Функтор и лямбда будут встроены, потому что они являются бесстатными объектами (вся информация доступна во время компиляции).

Указатели на функции и объекты boost :: Function (теперь в std: :) не могут быть встроены, поскольку во время компиляции не ясно, на какую функцию они указывают. Если они постоянные, вещи могут отличаться.

0 голосов
/ 01 февраля 2011

Может ли компилятор встроить вызовы? Да.

Будет ли это? Может быть. Проверьте после того, как вы знаете, что это имеет значение .

0 голосов
/ 01 февраля 2011

Возможно. Нет веских причин для этого или против него, это зависит только от того, что реализовали авторы компилятора.

...