Ответ 1
Проблема заключается не в встраивании, которое компилятор делает при каждой возможности. Проблема в том, что Visual С++, похоже, не понимает, что переменная указателя на самом деле является константой времени компиляции.
Test-случай:
// 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;
}
Функции разделяются на несколько единиц компиляции, чтобы обеспечить лучший контроль над inlining. В частности, я не хочу show_int
inlined, так как он делает код сборки беспорядочным.
Первым неприятным случаем является то, что действительный код (прокомментированная строка) отклоняется Visual С++. У g++ нет проблем с ним, но Visual С++ жалуется на "ожидаемое выражение постоянной компиляции". Это на самом деле хороший предиктор всего будущего поведения.
С включенной оптимизацией и нормальной семантикой компиляции (без кросс-модульной вставки) компилятор генерирует:
_main PROC ; COMDAT
; 18 : show_int( sum(1, 2) );
push 2
push 1
call _sum
push eax
call [email protected]@[email protected] ; show_int
; 19 : show_int( call(&sum, 3, 4) );
push 4
push 3
call _sum
push eax
call [email protected]@[email protected] ; show_int
; 20 : show_int( callt<&sum>(5, 6) );
push 6
push 5
call _sum
push eax
call [email protected]@[email protected] ; show_int
; 21 : show_int( (*sum_ptr)(1, 7) );
push 7
push 1
call DWORD PTR _sum_ptr
push eax
call [email protected]@[email protected] ; show_int
; 22 : show_int( call(sum_ptr, 3, 8) );
push 8
push 3
call DWORD PTR _sum_ptr
push eax
call [email protected]@[email protected] ; 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
, даже если исходный код использовал указатель на функцию.
Если теперь включить встраивание путем компиляции функций_pinter_resolution.cpp и sum.cpp с помощью /GL
и связывания с /LTCG
, мы обнаружим, что компилятор строит все прямые вызовы. Косвенные вызовы остаются как есть.
_main PROC ; COMDAT
; 18 : show_int( sum(1, 2) );
push 3
call [email protected]@[email protected] ; show_int
; 19 : show_int( call(&sum, 3, 4) );
push 7
call [email protected]@[email protected] ; show_int
; 20 : show_int( callt<&sum>(5, 6) );
push 11 ; 0000000bH
call [email protected]@[email protected] ; show_int
; 21 : show_int( (*sum_ptr)(1, 7) );
push 7
push 1
call DWORD PTR _sum_ptr
push eax
call [email protected]@[email protected] ; show_int
; 22 : show_int( call(sum_ptr, 3, 8) );
push 8
push 3
call DWORD PTR _sum_ptr
push eax
call [email protected]@[email protected] ; 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 С++ 2010 с пакетом обновления 1 (SP1), компиляция для x86, размещенная на x64.
Microsoft (R) 32-разрядная версия оптимизатора C/С++ версии 16.00.40219.01 для 80x86