2013-05-08 44 views
7

Trong C++, có hai cách để chuyển một hàm vào một hàm khác có vẻ tương đương.Chức năng C++ được truyền dưới dạng Đối số mẫu vs Tham số

#include <iostream> 

int add1(int i){ return i+1; } 
int add2(int i){ return i+2; } 

template <int (*T)(int) > 
void doTemplate(int i){ 
    std::cout << "Do Template: " << T(i) << "\n"; 
} 

void doParam(int i, int (*f)(int)){ 
    std::cout << "Do Param: " << f(i) << "\n"; 
} 

int main(){ 
    doTemplate<add1>(0); 
    doTemplate<add2>(0); 

    doParam(0, add1); 
    doParam(0, add2); 
} 

doTemplate chấp nhận hàm làm đối số mẫu, và doParam chấp nhận nó làm con trỏ hàm và cả hai đều cho kết quả tương tự.

Sự cân bằng giữa việc sử dụng từng phương pháp là gì?

+0

Hãy thử 'doParam (0, some_condition? Add1: add2)' để xem sự khác biệt. –

Trả lời

10

Phiên bản dựa trên mẫu cho phép trình biên dịch nội tuyến cuộc gọi, bởi vì địa chỉ của hàm được biết tại thời gian biên dịch. Rõ ràng, bất lợi là địa chỉ của hàm để được biết tại thời gian biên dịch (vì bạn đang sử dụng nó làm đối số mẫu) và đôi khi điều này có thể không thực hiện được.Điều đó đưa chúng ta đến trường hợp thứ hai, nơi mà con trỏ hàm chỉ có thể được xác định trong thời gian chạy, do đó làm cho trình biên dịch không thể thực hiện nội tuyến, nhưng cho bạn sự linh hoạt trong việc xác định thời gian chạy hàm được gọi là:

bool runtimeBooleanExpr = /* ... */; 
doParam(0, runtimeBooleanExpr ? add1 : add2); 

Thông báo, tuy nhiên, có một cách thứ ba:

template<typename F> 
void doParam(int i, F f){ 
    std::cout << "Do Param: " << f(i) << "\n"; 
} 

nào giúp bạn linh hoạt hơn và vẫn có lợi thế là biết tại thời gian biên dịch những gì chức năng sẽ được gọi là:

doParam(0, add1); 
doParam(0, add2); 

Và nó cũng cho phép đi qua bất kỳ đối tượng callable thay vì một con trỏ hàm:

doParam(0, my_functor()); 

int fortyTwo = 42; 
doParam(0, [=] (int i) { return i + fortyTwo; /* or whatever... */ } 

Để hoàn chỉnh, đó cũng là một thứ tư cách, sử dụng std::function:

void doParam(int x, std::function<int(int)> f); 

Trong đó có cùng một mức độ tổng quát (trong đó bạn có thể vượt qua bất kỳ đối tượng có thể gọi), nhưng cũng cho phép y để xác định đối tượng có thể gọi tại thời gian chạy - rất có thể với một hình phạt hiệu suất, vì (một lần nữa) nội tuyến trở thành không thể cho trình biên dịch.

Để thảo luận thêm về hai tùy chọn cuối cùng, hãy xem this Q&A on StackOverflow.

+0

bạn có thể giải thích '=' trong lambda? Tôi vẫn đang dụi mắt mình :-) –

+0

@Koushik: Nó ghi lại biến 'fortyTwo' theo giá trị. Một lambda không bắt giữ sẽ không phải là một ví dụ tốt, vì chúng có thể được chuyển đổi hoàn toàn thành các con trỏ hàm;) –

+0

[=] làm cho nó mặc định để ghi lại giá trị theo giá trị (thay vì tham chiếu). SO nó nắm bắt giá trị 42 cho fortyTwo chứ không phải là một tham chiếu đến nó. Điều đó có nghĩa là nếu bạn gán 111 cho nó thì lambda vẫn sẽ sử dụng giá trị 42 mà nó đã capture. – jcoder

5

thông số Template

  1. phải được biết vào thời điểm biên dịch.
  2. dẫn đến một chức năng instantation cho mỗi giá trị riêng biệt của tham số (cái gọi là mẫu sưng lên)
  3. hàm được gọi là transparant để trình biên dịch (nội tuyến, nhưng có thể dẫn đến sưng lên hơn nữa, đôi thanh kiếm lưỡi)
  4. chức năng gọi điện thoại có thể quá tải cho các giá trị cụ thể của tham số, mà không sửa đổi hiện đang

con trỏ hàm

  1. được thông qua vào thời điểm chạy.
  2. chỉ dẫn đến một chức năng gọi điện thoại (nhỏ mã đối tượng)
  3. hàm được gọi là thường đục để trình biên dịch (không nội tuyến)
  4. chức năng gọi điện thoại cần có một thời gian chạy if/switch để làm công cụ đặc biệt cho các giá trị đặc biệt của tham số, đây là giòn

Khi sử dụng phiên bản nào: nếu bạn cần tốc độ và rất nhiều tùy biến, sử dụng các mẫu. Nếu bạn cần sự linh hoạt trong thời gian chạy, nhưng không phải trong quá trình triển khai, hãy sử dụng các con trỏ hàm.

Khi @AndyProwl chỉ ra: nếu bạn có trình biên dịch C++ 11, con trỏ hàm được tổng quát thành các đối tượng có thể gọi như std::function và biểu thức lambda. Điều này mở ra một thể hoàn toàn mới của sâu (trong một ý nghĩa tốt).

Các vấn đề liên quan