2011-06-28 40 views
14

Sử dụng std::function, chúng tôi có thể nhận loại đối số bằng cách sử dụng các kiểu gõ argument_type, second_argument_type, v.v., nhưng tôi không thể thấy cách làm điều tương tự với lambdas. Có thể không? (Tôi đang sử dụng VS2010)Chúng ta có thể lấy kiểu đối số lambda không?

Nói rằng tôi muốn một cái gì đó như sau trong hệ thống deserialization tôi sử dụng để đọc một đối tượng và vượt qua nó để một hàm setter:

template<typename F> 
static void forward(F f) 
{ 
    // Create an object of the type of the first 
    // parameter to the function object F 
    typedef typename F::argument_type T; 
    T t; 

    //...do something with 't' here (deserialize in my case) 

    // Forward the object to the function 
    f(t); 
} 

Nó có thể được sử dụng như thế này và tất cả mọi thứ hoạt động tốt:

std::function<void(int)> f = [](int i) -> void { setValue(i); }; 
forward(f); 

Nhưng nó sẽ không làm việc trực tiếp với lambdas:

forward([](int i) -> void { setValue(i); }); 
//error C2039: 'argument_type' : is not a 
//member of '`anonymous-namespace'::<lambda1>' 

Có cách nào để truy cập các kiểu tham số theo cách sẽ hoạt động đối với cả hai đối tượng lambdas và std::function? Có lẽ một cách để có được loại std::function của một lambda đầu tiên, và sau đó là argument_type từ đó?


Tiếp theo từ câu trả lời dưới đây, một phiên bản làm việc với lambdas và std::function là:

template<typename T, typename F> 
static void forward(F f) 
{ 
    T t; 

    //...do something with 't' here (deserialize in my case) 

    f(t); 
} 

forward<int>([](int i) -> void { setValue(i); }); 

Kể từ int được lặp lại ở đây tôi đã hy vọng để thoát khỏi nó - không đến nỗi tệ cho int nhưng khó chịu hơn đối với các loại có tên dài trong một vài không gian tên. C'est la vie!

+2

Câu trả lời ngắn gọn: không. – ildjarn

+0

Câu trả lời dài: yup, xem bên dưới. –

Trả lời

16

Điều này không được mong muốn trong trường hợp chung. (Lưu ý rằng nó khá dễ dàng cho std::function<T(A)> để chỉ định ví dụ: argument_type là: nó chỉ là A! Nó có sẵn trong định nghĩa loại.)

Có thể yêu cầu mỗi loại đối tượng chức năng để xác định loại đối số của nó và lần lượt yêu cầu rằng các loại đóng cửa được tạo ra từ biểu thức lambda làm như vậy. Trong thực tế, các tính năng trước C++ 0x như các functors thích ứng sẽ chỉ hoạt động đối với các kiểu như vậy.

Tuy nhiên, chúng tôi đang chuyển từ đó với C++ 0x và vì lý do chính đáng. Đơn giản nhất trong số đó chỉ đơn giản là quá tải: một loại functor với một templated operator() (a.k.a một functor đa hình) đơn giản là lấy tất cả các loại đối số; vì vậy argument_type sẽ là gì? Một lý do khác là các mã chung (thường) cố gắng xác định các ràng buộc nhỏ nhất trên các kiểu và các đối tượng mà nó hoạt động trên để dễ dàng hơn (được sử dụng lại).

Nói cách khác, mã chung không phải là thực sự quan tâm là được cung cấp Functor f, typename Functor::argumentint. Đó là nhiều hơn nữa thú vị khi biết rằng f(0) là một biểu thức có thể chấp nhận được. Đối với C++ 0x này cung cấp các công cụ như decltypestd::declval (đóng gói thuận tiện hai bên trong std::result_of).

Cách tôi nhìn thấy bạn có hai lựa chọn: yêu cầu tất cả các thư viện được chuyển đến mẫu của bạn sử dụng quy ước kiểu C++ 03 chỉ định argument_type và các loại tương tự; sử dụng kỹ thuật dưới đây; hoặc thiết kế lại. Tôi muốn giới thiệu tùy chọn cuối cùng nhưng đó là cuộc gọi của bạn vì tôi không biết codebase của bạn trông như thế nào hoặc yêu cầu của bạn là gì.


Đối với loại functor đơn lẻ (nghĩa là không quá tải), bạn có thể kiểm tra thành viên operator(). Điều này làm việc cho các loại đóng của các biểu thức lambda.

Vì vậy, chúng tôi tuyên bố những người giúp đỡ

template<typename F, typename Ret, typename A, typename... Rest> 
A 
helper(Ret (F::*)(A, Rest...)); 

template<typename F, typename Ret, typename A, typename... Rest> 
A 
helper(Ret (F::*)(A, Rest...) const); 

// volatile or lvalue/rvalue *this not required for lambdas (phew) 

chấp nhận một con trỏ tới hàm thành viên tham gia ít nhất một đối số. Và bây giờ:

template<typename F> 
struct first_argument { 
    typedef decltype(helper(&F::operator())) type; 
}; 

[. Một đặc điểm công phu liền có thể truy vấn vế trái-rvalue/const/quá tải không ổn định và vạch trần đối số đầu tiên nếu nó giống nhau cho tất cả các quá tải, hoặc sử dụng std::common_type]

+0

Cảm ơn lời giải thích tuyệt vời. Tôi đã chọn thiết kế lại (loại 'T' cũng là một tham số mẫu) nhưng tôi chỉ hy vọng tôi có thể loại bỏ nó bằng cách nào đó vì nó có vẻ như trùng lặp không cần thiết. –

+0

Mã thực sự của tôi thực sự đã có các biến bị bắt (thường là đối tượng mà trên đó để gọi phương thức setter) nhưng nhờ làm rõ. –

+1

Bạn không thể làm điều gì đó tương tự như vậy, nhưng với '& F :: operator()' và các hàm con trỏ thành thành viên thay vì phân tích 'F' trực tiếp? Điều đó sẽ làm việc cho lambdas chấp nhận chụp là tốt, tôi sẽ nghĩ. –

2

@ Lục của Câu trả lời là rất tốt nhưng tôi chỉ tình cờ gặp một trường hợp mà tôi cũng cần thiết để đối phó với con trỏ hàm:

template<typename Ret, typename Arg, typename... Rest> 
Arg first_argument_helper(Ret(*) (Arg, Rest...)); 

template<typename Ret, typename F, typename Arg, typename... Rest> 
Arg first_argument_helper(Ret(F::*) (Arg, Rest...)); 

template<typename Ret, typename F, typename Arg, typename... Rest> 
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const); 

template <typename F> 
decltype(first_argument_helper(&F::operator())) first_argument_helper(F); 

template <typename T> 
using first_argument = decltype(first_argument_helper(std::declval<T>())); 

này có thể được sử dụng trên cả functors và con trỏ hàm:

void function(float); 

struct functor { 
    void operator() (int); 
}; 

int main() { 
    std::cout << std::is_same<first_argument<functor>, int>::value 
       << ", " 
       << std::is_same<first_argument<decltype(&function)>, int>::value 
       << std::endl; 
    return 0; 

} 
+0

Ban đầu tôi đã báo cáo một vấn đề với điều này, nhưng bây giờ tôi nhận ra rằng tôi đã phạm sai lầm. [Ở đây nó đang trực tuyến] (http://coliru.stacked-crooked.com/a/7660f42b6e2a7476). – doug65536

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