問題は、コンパイラがあらゆる機会に行うインライン化ではありません。問題は、ポインター変数が実際にはコンパイル時の定数であることを 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
てリンクすることでインライン化を有効にする/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);
すべてのテストは、x64 でホストされている x86 用にコンパイルされた Visual C++ 2010 Service Pack 1 で実行されます。
Microsoft (R) 32 ビット C/C++ 最適化コンパイラ バージョン 16.00.40219.01 for 80x86