问题不在于内联,编译器在每一个机会都会这样做。问题是 Visual C++ 似乎没有意识到指针变量实际上是一个编译时常量。
测试用例:
// function_pointer_resolution.cpp : Defines the entry point for the console application.
//
extern void show_int( int );
extern "C" typedef int binary_int_func( int, int );
extern "C" binary_int_func sum;
extern "C" binary_int_func* const sum_ptr = sum;
inline int call( binary_int_func* binary, int a, int b ) { return (*binary)(a, b); }
template< binary_int_func* binary >
inline int callt( int a, int b ) { return (*binary)(a, b); }
int main( void )
{
show_int( sum(1, 2) );
show_int( call(&sum, 3, 4) );
show_int( callt<&sum>(5, 6) );
show_int( (*sum_ptr)(1, 7) );
show_int( call(sum_ptr, 3, 8) );
// show_int( callt<sum_ptr>(5, 9) );
return 0;
}
// sum.cpp
extern "C" int sum( int x, int y )
{
return x + y;
}
// show_int.cpp
#include <iostream>
void show_int( int n )
{
std::cout << n << std::endl;
}
这些函数被分成多个编译单元,以便更好地控制内联。具体来说,我不想show_int
内联,因为它会使汇编代码变得混乱。
第一个问题是有效代码(注释行)被 Visual C++ 拒绝。 G++ 没有问题,但 Visual C++ 抱怨“预期的编译时常量表达式”。这实际上是对所有未来行为的良好预测。
在启用优化和正常编译语义(无跨模块内联)的情况下,编译器生成:
_main PROC ; COMDAT
; 18 : show_int( sum(1, 2) );
push 2
push 1
call _sum
push eax
call ?show_int@@YAXH@Z ; show_int
; 19 : show_int( call(&sum, 3, 4) );
push 4
push 3
call _sum
push eax
call ?show_int@@YAXH@Z ; show_int
; 20 : show_int( callt<&sum>(5, 6) );
push 6
push 5
call _sum
push eax
call ?show_int@@YAXH@Z ; show_int
; 21 : show_int( (*sum_ptr)(1, 7) );
push 7
push 1
call DWORD PTR _sum_ptr
push eax
call ?show_int@@YAXH@Z ; show_int
; 22 : show_int( call(sum_ptr, 3, 8) );
push 8
push 3
call DWORD PTR _sum_ptr
push eax
call ?show_int@@YAXH@Z ; show_int
add esp, 60 ; 0000003cH
; 23 : //show_int( callt<sum_ptr>(5, 9) );
; 24 : return 0;
xor eax, eax
; 25 : }
ret 0
_main ENDP
sum_ptr
使用和不使用之间已经存在巨大差异sum_ptr
。使用语句sum_ptr
生成间接函数调用call DWORD PTR _sum_ptr
,而所有其他语句生成直接函数调用call _sum
,即使源代码使用函数指针也是如此。
如果我们现在通过编译 function_pointer_resolution.cpp 和 sum.cpp/GL
并链接 with来启用内联/LTCG
,我们会发现编译器内联了所有直接调用。间接呼叫保持原样。
_main PROC ; COMDAT
; 18 : show_int( sum(1, 2) );
push 3
call ?show_int@@YAXH@Z ; show_int
; 19 : show_int( call(&sum, 3, 4) );
push 7
call ?show_int@@YAXH@Z ; show_int
; 20 : show_int( callt<&sum>(5, 6) );
push 11 ; 0000000bH
call ?show_int@@YAXH@Z ; show_int
; 21 : show_int( (*sum_ptr)(1, 7) );
push 7
push 1
call DWORD PTR _sum_ptr
push eax
call ?show_int@@YAXH@Z ; show_int
; 22 : show_int( call(sum_ptr, 3, 8) );
push 8
push 3
call DWORD PTR _sum_ptr
push eax
call ?show_int@@YAXH@Z ; show_int
add esp, 36 ; 00000024H
; 23 : //show_int( callt<sum_ptr>(5, 9) );
; 24 : return 0;
xor eax, eax
; 25 : }
ret 0
_main ENDP
底线:是的,只要不是从变量中读取该函数指针,编译器就会通过编译时常量函数指针进行内联调用。 这种函数指针的使用得到了优化:
call(&sum, 3, 4);
但这没有:
(*sum_ptr)(1, 7);
所有测试都使用 Visual C++ 2010 Service Pack 1 运行,为 x86 编译,托管在 x64 上。
Microsoft (R) 32 位 C/C++ 优化编译器版本 16.00.40219.01 用于 80x86