2010-10-25 59 views
12

Tôi không muốn con trỏ hàm trên cao, tôi chỉ muốn cùng mã cho hai chức năng khác nhau với cùng một chữ ký:Có thể có hàm (-name) làm tham số mẫu trong C++ không?

void f(int x); 
void g(int x); 

... 

template<typename F> 
void do_work() 
{ 
    int v = calculate(); 
    F(v); 
} 

... 

do_work<f>(); 
do_work<g>(); 

Đây có phải là có thể?


Để làm sáng tỏ có thể nhầm lẫn: Với "mẫu số" Ý tôi là tham số/tranh luận với mẫukhông một tham số chức năng có loại là templated.

Trả lời

6

Một cách tiếp cận đó là rất có khả năng để tạo ra các chức năng cuộc gọi trực tiếp, bởi vì nó mang lại cho trình biên dịch không có lựa chọn, là để sử dụng hàm thành viên tĩnh:

struct F { static void func(int x) { /*whatever*/ } }; 
struct G { static void func(int x) { /*whatever*/ } }; 

template<class T> 
void do_work() { 
    T::func(calculate()); 
} 

Không có con trỏ hàm, không có thời gian và không cần thiết this. Tôi đảm bảo không có gì, tất nhiên, nhưng mã được tạo nên hợp lý ngay cả khi tối ưu hóa bị vô hiệu hóa.

+0

+1: giải pháp rất đẹp. Không áp dụng được trong vấn đề trả lời câu hỏi của tôi, nhưng rất hay như một giải pháp chung! –

9

Bạn có thể có con trỏ để hoạt động dưới dạng tham số mẫu, nhưng đối tượng hàm có nhiều "C++ ish". Tuy nhiên, bạn có thể viết mẫu chức năng của bạn trong một cách mà chấp nhận cả hai biến thể:

#include <iostream> 

void f(int x) 
{ 
    std::cout << "inside function f\n"; 
} 

struct g 
{ 
    void operator()(int x) 
    { 
     std::cout << "inside function object g\n"; 
    } 
}; 

template <typename Functor> 
void do_work(Functor fun) 
{ 
    fun(42); 
} 

int main() 
{ 
    // template argument is automatically deduced 
    do_work(&f); 
    // but we could also specify it explicitly 
    do_work<void(*)(int)>(&f); 

    // template argument is automatically deduced 
    do_work(g()); 
    // but we could also specify it explicitly 
    do_work<g>(g()); 
} 

Ở đây, tên Functor gợi ý tại bất kỳ loại đó là callable qua f(x) cú pháp. Các hàm hỗ trợ cú pháp này một cách tự nhiên và trong trường hợp đối tượng hàm, f(x) là cú pháp đường cho f.operator()(x).

+1

+1 ... và chúng biên dịch thành một cuộc gọi trực tiếp mà không phải trả phí gọi hàm trên thời gian chạy. – Doug

+0

@Doug: điều đó không được đảm bảo. Rất có thể, mặc dù, ví dụ: 'std :: sort' mang lại nhiều lợi ích từ việc tối ưu hóa đó. – MSalters

+0

Việc triển khai của bạn sẽ khớp với tham số 'typename Fun' với hàm nguyên mẫu' void (*) (int) ', và sau đó sử dụng cùng một instantiation cho cả hai cuộc gọi - không phải nội tuyến: đó là lý do tại sao bạn thực sự kết thúc con trỏ hàm một đối số thời gian chạy cho 'do_work (Fun fun)'. –

6

Không, bạn cần bọc các hàm trong lớp bao bọc với operator(). Dưới đây là ví dụ:

class Functor_f 
{ 
public: 
    void operator()(int x) 
    { 
    } 
}; 

class Functor_g 
{ 
    public: 
    void operator()(int x) 
    { 
    } 
}; 



template<typename F> 
void do_work() 
{ 
    F f; 
int v = calculate(); 
    f(v); 
} 


int main() 
{ 
    do_work<Functor_f>(); 
    do_work<Functor_g>(); 

} 

Bạn có thể sử dụng std::ptr_fun để tự động gói này cho bạn. Ví dụ:

void f(int x) 
{ 
} 

void g(int x) 
{ 
} 

template<typename F> 
void do_work(F f) 
{ 
int v = calculate(); 
    f(v); 
} 


int main() 
{ 
    do_work(std::ptr_fun(f)); 
    do_work(std::ptr_fun(g)); 

} 
+0

Ví dụ đầu tiên (không có 'std :: ptr_fun') cho cơ hội tốt hơn về nội tuyến thực tế - và (mặc dù tất nhiên nó phụ thuộc rất nhiều) do đó rất có thể nhanh nhất, phần nào phản trực giác có lẽ. Chúc mừng, –

21

Ý tưởng của bạn là ok, nhưng bạn không được chuyển một loại mà là một giá trị (cụ thể là con trỏ hàm> Ngoài ra, hãy chuyển chính sách mẫu cung cấp chức năng - bạn nên đọc C++ Thiết kế bởi Andrei Alexandrescu.

#include <iostream> 

int f(int x) { return 2 * x; } 
int g(int x) { return -3 * x; } 

typedef int (*F)(int); 

template<F f> 
int do_work() 
{ 
    return f(7); 
} 

int main() 
{ 
    std::cout << do_work<f>() << '\n' 
       << do_work<g>() << '\n'; 
} 

HOẶC

int calculate() { return 4; } 

struct F { int do_something_with(int x) { return 2 * x; } }; 
struct G { int do_something_with(int x) { return -3 * x; } }; 
// or, make these functions static and use Operator::do_something_with() below... 

template<typename Operation> 
int do_work() 
{ 
    int v = calculate(7); 
    return Operation().do_something_with(v); 
} 

int main() 
{ 
    std::cout << do_work<F>() << '\n' 
       << do_work<G>() << '\n'; 
} 
+0

+1 để hiển thị giải pháp tốt với các con trỏ fn. Nhưng tôi giả định này vẫn sẽ phải chịu một số chi phí thời gian chạy để dereference con trỏ fn so với một phiên bản sao chép & dán cho do_work_f() và do_work_g() ...? –

+1

@Martin: đó là một câu hỏi quan trọng - tất cả đều là người tối ưu. Tôi sẽ không đặt cược vào điều này được inlined, trong khi tôi sẽ đặt cược vào một thành viên mẫu chính sách .... –

+0

@Tony - có thể bạn có thể liên kết trong một số tài nguyên mà (xấp xỉ) mô tả một điều chính sách mẫu theo cách bạn sẽ áp dụng nó cho vấn đề này? –

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