2011-09-30 42 views
5

Tôi đang cố viết hàm mẫu chấp nhận std::function phụ thuộc vào đối số mẫu. Thật không may trình biên dịch không có khả năng khử chính xác các đối số cho số std::function. Dưới đây một số mã ví dụ đơn giản:Chức năng mẫu C++ 11 lấy hàm std :: phụ thuộc vào tham số mẫu

#include <iostream> 
#include <functional> 

using namespace std; 

void DoSomething(unsigned ident, unsigned param) 
{ 
    cout << "DoSomething called, ident = " << ident << ", param = " << param << "\n"; 
} 

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, std::function< void (Ident, Param) > op) 
{ 
    op(ident, param); 
} 

int main() 
{ 
    unsigned id(1); 
    unsigned param(1); 

    // The following fails to compile 
    // CallFunc(id, param, DoSomething); 

    // this is ok 
    std::function< void (unsigned, unsigned) > func(DoSomething); 
    CallFunc(id, param, func); 

    return 0; 
} 

Nếu tôi gọi là mẫu như sau:

CallFunc(id, param, DoSomething); 

tôi nhận được các lỗi sau đây:

function-tpl.cpp:25: error: no matching function for call to CallFunc(unsigned int&, unsigned int&, void (&)(unsigned int, unsigned int))

Nếu tôi một cách rõ ràng tạo ra một std :: chức năng của loại chính xác (hoặc bỏ qua) vấn đề biến mất:

std::function< void (unsigned, unsigned) > func(DoSomething); 
CallFunc(id, param, func); 

Tôi làm cách nào để viết mã này để tạm thời không cần thiết?

Trả lời

2

Nếu bạn đang sử dụng các mẫu, bạn có thể tránh std::function hoàn toàn, trừ khi vì một lý do bạn muốn giới hạn cụ thể chức năng để lấy std::function:

template < typename Ident, typename Param, typename Func > 
void CallFunc(Ident ident, Param param, Func op) 
{ 
    op(ident, param); 
} 
+0

Bạn có quên xóa các ký tự nhận xét "//" trước dòng vi phạm không? Tôi có GCC4.6.1 và GCC từ chối nói dòng. –

+0

@litb * D'OH!* Được phát hiện. –

0

Bạn có thể thực hiện chuyển đổi nội dòng hoặc sử dụng bind. Không phải là đặc biệt xinh đẹp, nhưng họ hoàn thành công việc:

CallFunc(id, param, std::function<void(unsigned, unsigned)>(DoSomething)); 

CallFunc(id, param, std::bind(DoSomething, std::placeholders::_1, std::placeholders::_2)); 

+0

Phần tử std :: bind tạo lỗi sau: function-tpl.cpp: 37: error: không có hàm nào phù hợp cho cuộc gọi đến 'CallFunc (unsigned int &, unsigned int &, std :: _ Bind , std :: _ Placeholder <2>)) (unsigned int, unsigned int)>) ' – mark

+0

@mark: Bạn nói đúng, 'bind' không cho phép trích đối số và bạn phải nói' CallFunc < unsigned, unsigned> (...) '. Vì vậy ... chỉ cần sử dụng chuyển đổi rõ ràng. –

8

Bạn cần phải thực hiện tham số hàm thứ ba một ngữ cảnh không suy luận cho các tham số mẫu trong đó. Sau đó trình biên dịch sẽ không so sánh kiểu đối số với kiểu tham số mà không xem xét tất cả các chuyển đổi ngầm định (tiêu chuẩn nói, và C++ 0x làm rõ hơn nữa, cho tham số hàm không có tham số mẫu trong các vị trí suy luận, tất cả ngầm định chuyển đổi được cho phép trong cầu nối một sự khác biệt).

template < typename T > struct id { typedef T type; }; 

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, 
       typename id<std::function< void (Ident, Param) >>::type op) 
{ 
    op(ident, param); 
} 

Thay vì id bạn có thể sử dụng boost::identity. Trong C++ 0x và trình biên dịch hỗ trợ nó, bạn có thể có một phiên bản dễ đọc hơn sử dụng bí danh mẫu

template < typename T > using nondeduced = typename id<T>::type; 

Sau đó, mã của bạn trở nên đơn giản

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, 
       std::function< nondeduced<void (Ident, Param)> > op) 
{ 
    op(ident, param); 
} 

Tuy nhiên GCC chưa hỗ trợ các mẫu bí danh.

+0

Tôi đã có thể nghĩ rằng tham số trong một bối cảnh không suy luận đã. Điều này là tốt để biết! –

+0

Cách phiên bản đầu tiên bao bọc toàn bộ loại 'chức năng', nhưng phiên bản thứ hai chỉ bao bọc thông số mẫu? –

+0

@KerrekSB vì các cách diễn giải khác nhau trong các trình biên dịch của từ ngữ trong C++ 03, tôi đã bao bọc toàn bộ kiểu tham số vào một ngữ cảnh không được suy luận. Một số trình biên dịch sẽ chọn các phần phức tạp của kiểu trước khi nhập vào bối cảnh không suy luận (tức là các phần 'std :: function ') và so sánh với đối số, gây ra lỗi khấu trừ . Các trình biên dịch khác sẽ không làm điều đó. Nếu quấn quanh toàn bộ loại, không có phần "không bị ảnh hưởng" nào bị bỏ lại, vì vậy tất cả là '...' (lỗ đen), và không có sự không phù hợp nào có thể xảy ra. Xem http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1184 –

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