Sự khác biệt giữa các cách khác nhau để truyền một chức năng như là một đối số cho một chức năng khác là gì?


8

Tôi có một tình huống trong đó một hàm gọi một trong số các hàm có thể. Đây có vẻ là một nơi tốt để truyền một hàm như một tham số. Trong câu trả lời Quoara này của Zubkov, có ba cách để làm điều này.

int g(int x(int)) { return x(1); }
int g(int (*x)(int)) { return x(1); }
int g(int (&x)(int)) { return x(1); }
...
int f(int n) { return n*2; }
g(f); // all three g's above work the same

Khi nào nên sử dụng phương pháp nào? Có gì khác biệt? Tôi thích cách tiếp cận đơn giản nhất vậy tại sao không nên sử dụng cách đầu tiên?

Đối với tình huống của tôi, hàm chỉ được gọi một lần và tôi muốn giữ cho nó đơn giản. Tôi có nó làm việc với các đường chuyền của con trỏ và tôi chỉ gọi nó với g(myFunc)nơi myFunclà chức năng mà được gọi là cuối cùng.


3
Không một ai trong số họ. Sử dụng một tham số mẫu.
LF

2
Hai cái đầu hoàn toàn tương đương. Cái thứ ba gần giống như cái thứ hai, ngoại trừ nó yêu cầu một giá trị. g(+f);làm việc cho hai cái đầu tiên, nhưng không phải cái thứ ba
Raymond Chen

@RaymondChen "Hai cái đầu tiên hoàn toàn tương đương" thì theo quan điểm của tôi thì cái đầu tiên rõ ràng là sự lựa chọn chính xác vì nó đơn giản hơn. Tại sao làm phức tạp nó với một con trỏ?
miền Bắc

1
Mặt khác, trong int g(int x(int)), xlà một con trỏ mặc dù nó không giống như một con trỏ. Khai báo toàn cầu tương ứng khai int x(int);báo một hàm, không phải là một con trỏ hàm.
Raymond Chen

Một liên kết thần thánh để sao lưu yêu cầu của @ RaymondChen . Lưu ý rằng các nhãn lắp ráp phát ra xnhư là một con trỏ quá.
Eric

Câu trả lời:


3

Mở rộng nhận xét của LF, thường tốt hơn là tránh hoàn toàn các con trỏ hàm và hoạt động theo các đối tượng bất khả xâm phạm (những thứ xác định operator()). Tất cả những điều sau đây cho phép bạn làm điều đó:

#include <type_traits>

// (1) unrestricted template parameter, like <algorithm> uses
template<typename Func>
int g(Func x) { return x(1); }

// (2) restricted template parameter to produce possibly better errors
template<
    typename Func,
    typename=std::enable_if_t<std::is_invocable_r_v<int, Func, int>>
>
int g(Func x) { return std::invoke(x, 1); }

// (3) template-less, trading a reduction in code size for runtime overhead and heap use
int g(std::function<int(int)> x) { return x(1); }

Điều quan trọng, tất cả những thứ này có thể được sử dụng trên các hàm lambda với các ảnh chụp, không giống như bất kỳ tùy chọn nào của bạn:

int y = 2;
int ret = g([y](int v) {
    return y + v;
});

Chúng được gọi như thế nào? Ngoài ra làm thế nào là tốt hơn?
miền Bắc

Chúng được gọi chính xác giống như chữ ký của bạn ở trên. Chúng tốt hơn bởi vì chúng hoạt động với lambdas và các chức năng nhà nước khác. Lưu ý rằng tất cả <algorithm>sử dụng phương pháp này để chấp nhận các chức năng gọi lại.
Eric

Đồng ý. Để xác nhận, bạn không cần gọi hàm templated bằng mã định danh mẫu? Ví dụ bạn không cần g<int>(myFunc)chỉ g(myFunc)?
miền Bắc

Đúng, ý tưởng là để cho tham số tyoe được suy ra
Eric

Các mẫu vẫn hoạt động nếu bạn truyền nhiều hơn một hàm làm tham số?
miền Bắc
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.