假设我有一个函数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)lambda函数:
[](){ std::cout << "lambda!" << std::endl; }
int main( )
{
functionProxy( Functor() );
functionProxy( function );
functionProxy( [](){ std::cout << "lambda!" << std::endl; } );
return 0;
}
在上述所有情况下,编译器是否能够在function
内内联functionProxy
?
答案 0 :(得分:28)
当然可以。
它知道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 Release。)
答案 1 :(得分:0)
可能。没有强烈的理由支持或反对它,它只取决于编译器编写者实现的内容。
答案 2 :(得分:0)
尝试了以下模板指针到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++; };
...
答案 3 :(得分:-1)
Functor和lambda将被内联,因为它们是无状态对象(所有信息都在编译时可用)。
函数指针和boost ::函数对象(现在在std::)中无法内联,因为在编译时它们不清楚它们指向的是什么函数。如果它们是常数,则可能会有所不同。
答案 4 :(得分:-4)
编译器能是否可以内联调用?是。
是的吗?也许。请在know it matters之后检查。