假设我有一个函数
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
?
当然可以。
它知道
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,普通版本编译。)
有可能。没有充分的理由支持或反对它,它只取决于编译器编写者实现的内容。
已尝试以下模板化指针到 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 均使用
widget.f()
内联 (*lambda_)()
和 -O2
调用。也就是说,根据反汇编的二进制文件,a
直接在main()
内部递增。
即使使用非常量
lambda
和 lambda_
指针,也可以应用内联(删除两个 const
)。
同上,使用局部变量和 lambda 捕获:
int main() {
volatile int a = 0;
auto lambda = [&a](){ a++; };
...
编译器能够内联调用吗? 是的。
会吗? 或许。 知道这很重要后检查。