Vấn đề không phải là với nội tuyến, mà trình biên dịch thực hiện ở mọi cơ hội. Vấn đề là Visual C++ dường như không nhận ra rằng biến con trỏ thực sự là một hằng số biên dịch.
thử nghiệm hợp cụ thể:
// 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;
}
Các chức năng được tách ra thành nhiều đơn vị biên soạn để cung cấp cho kiểm soát tốt hơn nội tuyến. Cụ thể, tôi không muốn show_int
được gạch chân, vì nó làm cho mã lắp ráp lộn xộn.
Lỗi đầu tiên của sự cố là mã hợp lệ (dòng nhận xét) bị từ chối bởi Visual C++. G++ has no problem with it, nhưng Visual C++ than phiền "biểu thức hằng số biên dịch mong đợi". Đây thực sự là một yếu tố dự báo tốt cho mọi hành vi trong tương lai.
Với tối ưu hóa kích hoạt và ngữ nghĩa tổng hợp bình thường (không cross-mô-đun nội tuyến), trình biên dịch tạo ra:
_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
Có đã là một sự khác biệt lớn giữa việc sử dụng sum_ptr
và không sử dụng sum_ptr
. Các câu lệnh sử dụng sum_ptr
tạo ra một cuộc gọi hàm gián tiếp call DWORD PTR _sum_ptr
trong khi tất cả các câu lệnh khác tạo ra một lệnh gọi hàm trực tiếp call _sum
, ngay cả khi mã nguồn đã sử dụng một con trỏ hàm.
Nếu bây giờ chúng tôi cho phép nội tuyến bằng cách biên dịch function_pointer_resolution.cpp và sum.cpp với /GL
và liên kết với /LTCG
, chúng tôi thấy rằng trình biên dịch inlines tất cả các cuộc gọi trực tiếp. Cuộc gọi gián tiếp giữ nguyên trạng thái.
_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
mấu chốt: Vâng, trình biên dịch thực hiện các cuộc gọi nội tuyến thực hiện thông qua một thời gian biên dịch con trỏ hàm liên tục, chừng nào mà con trỏ hàm không được đọc từ một biến. Việc sử dụng một con trỏ hàm đã được tối ưu hóa:
call(&sum, 3, 4);
nhưng điều này không:
(*sum_ptr)(1, 7);
Tất cả các bài kiểm tra chạy với Visual C++ 2010 Service Pack 1, biên soạn cho x86, lưu trữ trên x64.
Microsoft (R) 32-bit C/C++ Compiler Tối ưu hóa Version 16.00.40219.01 cho 80x86
gì sẽ gcc làm gì? –
@ Otávio Décio Tôi không có trình biên dịch GCC nên tôi xin lỗi tôi không biết! –
Vì bạn đang sử dụng trình biên dịch độc quyền, không chắc ai biết câu trả lời sẽ có thể cho bạn biết nếu không có hậu quả pháp lý nghiêm trọng. –