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.
精彩评论