2010-02-08 31 views
13

Trong wikipedia article about function objects nó nói các đối tượng như vậy có lợi thế về hiệu suất khi được sử dụng với for_each vì trình biên dịch có thể "nội tuyến" chúng.Trong C++ nó có nghĩa là gì đối với một trình biên dịch để "nội tuyến" một đối tượng hàm?

Tôi hơi mờ về chính xác điều này có ý nghĩa gì trong ngữ cảnh này ... hoặc bất kỳ bối cảnh nào tôi cảm thấy xấu hổ khi nói. Cảm ơn vì bất kì sự giúp đỡ!

Trả lời

11

Thông số cuối cùng của mẫu for_eachfunctor. Functor là một cái gì đó có thể được "gọi" bằng cách sử dụng toán tử () (có thể có đối số). Theo defintion, có hai loại functors riêng biệt:

  1. Chức năng không phải là thành viên thông thường là functors.
  2. Đối tượng loại lớp có quá tải () toán tử (được gọi là đối tượng chức năng) cũng là hàm.

Bây giờ, nếu bạn muốn sử dụng chức năng bình thường như một functor cho for_each, nó sẽ trông giống như sau

inline void do_something(int &i) { /* do something */ } 

int main() { 
    int array[10]; 
    std::for_each(array, array + 10, &do_something); 
} 

Trong trường hợp này các mẫu for_each được khởi tạo với các đối số [suy luận] <int *, void (*)(int &)> . Lưu ý rằng giá trị functor thực tế trong trường hợp này là con trỏ hàm &do_something được truyền làm đối số hàm. Từ quan điểm của hàm for_each, đây là giá trị thời gian chạy. Và vì nó là một giá trị thời gian chạy, các cuộc gọi đến hàm functor không thể được gạch chân. (Cũng giống như nó là trong trường hợp chung không thể inline bất kỳ cuộc gọi được thực hiện thông qua một con trỏ hàm).

Nhưng nếu chúng ta sử dụng một đối tượng hàm thay vào đó, các mã có thể trông như sau

struct do_something { 
    void operator()(int &i) { /* do something */ } 
}; 

int main() { 
    int array[10]; 
    std::for_each(array, array + 10, do_something()); 
} 

Trong trường hợp này các mẫu for_each được khởi tạo với các đối số [suy luận] <int *, do_something>. Các cuộc gọi đến functor từ bên trong for_each sẽ được chuyển đến do_something::operator(). Mục tiêu cho cuộc gọi được biết và cố định tại thời gian biên dịch. Vì hàm đích được biết tại thời gian biên dịch, cuộc gọi có thể dễ dàng được nội tuyến.

Trong trường hợp thứ hai, tất nhiên, cũng có giá trị thời gian chạy được chuyển làm đối số cho for_each. Đó là trường hợp [có thể "giả" tạm thời] của lớp do_something mà chúng tôi tạo khi gọi for_each. Nhưng giá trị thời gian chạy này không ảnh hưởng đến mục tiêu cho cuộc gọi (trừ khi operator() là ảo), vì vậy nó không ảnh hưởng đến nội tuyến.

+0

Nhưng định nghĩa đầy đủ của ':: std :: for_each' có sẵn cho trình biên dịch. Để một trình biên dịch thông minh, nó sẽ thấy rằng nó đang được gọi với một đối số cụ thể xảy ra là địa chỉ của một hàm được khai báo nội tuyến. Trình biên dịch sẽ có thể nội tuyến cuộc gọi hàm cũng như nếu nó là một phương thức lớp. – Omnifarious

+2

@Omnifarious: Trong ví dụ của tôi, bạn có thể gọi 'for_each' với các hàm khác nhau có chữ ký' void (int &) 'và trình biên dịch buộc phải sử dụng cùng một, một và chỉ một instantiation của' for_each': 'for_each AnT

+2

Ví dụ (quên các mẫu), khi tôi có hàm 'void foo (int)' trong chương trình của tôi và gọi nó nhiều lần là 'foo (1); foo (2); foo (3); 'Tôi thường mong đợi trình biên dịch tạo ra một thân hàm cho' foo' và thi hành cùng một đối số với các đối số khác nhau: 1, 2 và 3. Nếu tôi phát hiện ra trình biên dịch thay vì tạo ra 3 phần tử 'foo khác nhau 'với" nội tuyến "hằng số 1, 2 và 3 tương ứng, tôi sẽ ngạc nhiên một cách khó chịu. Đối với 'foo' nhỏ có thể được chấp nhận (nếu nó được gạch chân sau đó), nhưng đối với một' foo' lớn hơn thì điều này là không mong muốn. Những gì bạn mô tả về cơ bản là giống nhau. – AnT

2

Nội tuyến chỉ có nghĩa là thay thế mọi cuộc gọi đến hàm đó bằng nội dung của hàm đó trực tiếp.

Đó là một tối ưu hóa cho các chức năng nhỏ vì nó làm giảm chi phí nhảy tới một chức năng mới và sau đó quay trở lại.

3

Điều này có nghĩa là định nghĩa của hàm (mã) có thể được sao chép và tiết kiệm cho bạn từ một cuộc gọi hàm (được gọi là đắt tiền trên một số hệ thống). Hãy suy nghĩ về thay thế vĩ mô.

4

Nội tuyến là quá trình mà trình biên dịch có thể thay thế cuộc gọi đến một hàm có nội dung của chính hàm đó. Nó yêu cầu trình biên dịch biết nội dung của hàm khi nó được biên dịch.

Trình biên dịch thường không thể thực hiện điều này nếu con trỏ hàm được truyền.

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