2017-08-17 14 views
17

tôi muốn tuyên bố một chức năng như thế này:Tôi có thể sử dụng Null Lambda bằng C++ như thế nào?

template <typename Lambda> 
int foo(Lambda bar) { 
    if(/* check bar is null lambda */) 
     return -1; 
    else 
     return bar(3); 
} 

int main() { 
    std::cout << foo([](int a)->int{return a + 3;}) << std::endl; 
    std::cout << foo(NULL_LAMBDA) << std::endl; 
} 

Sau đó, làm thế nào tôi có thể tuyên bố NULL_LAMBDA và điều kiện kiểm tra thông qua chức năng lambda dù là null?

+19

Ý anh là gì bởi "lambda null"? – melpomene

+1

Bạn có thể sử dụng 'std :: optional' hoặc tương đương từ một thư viện khác không? – KABoissonneault

Trả lời

26

Bạn có thể thêm một chuyên môn hóa chuyên dụng:

#include <iostream> 
#include <cstddef> 

template<typename Lambda> int 
foo(Lambda bar) 
{ 
    return(bar(3)); 
} 

template<> int 
foo<::std::nullptr_t>(::std::nullptr_t) 
{ 
    return(-1); 
} 

int main() 
{ 
    ::std::cout << foo([] (int a) -> int {return(a + 3);}) << ::std::endl; 
    ::std::cout << foo(nullptr) << ::std::endl; 
} 
+0

Có lợi ích gì đối với chuyên môn về mẫu so với quá tải không phải mẫu có 'nullptr_t' không? – LWimsey

+3

@LWimsey Có chuyên môn về mẫu sẽ làm cho nó có thể gọi đúng 'foo' khi đối số mẫu được cung cấp rõ ràng. – VTT

+1

Vui lòng không sử dụng chuyên môn tại đây. Nó cho biết thêm không có gì hơn là chỉ có một quá tải bình thường và chỉ có thể dẫn đến các vấn đề nếu bạn thêm một quá tải khác nhau. – Barry

8

Trong trường hợp đặc biệt này, bạn chỉ có thể xác định đóng cửa rỗng của bạn là một trong đó luôn luôn trả -1:

template <typename Lambda> 
int foo(Lambda bar) { 
    return bar(3); 
} 

#include <iostream> 
int main() { 
    auto const NULL_LAMBDA = [](int){ return -1; }; 
    std::cout << foo([](int a) {return a + 3;}) << std::endl; 
    std::cout << foo(NULL_LAMBDA) << std::endl; 
} 

Khả năng là nếu bạn đang chọn tại thời gian chạy việc triển khai nào cần thực hiện, sau đó bạn nên loại bỏ nó tốt hơn bằng cách std::function thay vì tạo mẫu. Và std::function được phép là trống - nó có thể được gán và so sánh với một con trỏ rỗng.


Nếu bạn biết lúc biên soạn rằng một số trang web gọi sẽ luôn vượt qua lambda 'null', sau đó bạn có thể chuyên thực hiện một cách thích hợp. Các tùy chọn rõ ràng bao gồm quá tải foo() với phiên bản không tham số bar hoặc chuyên môn với triển khai khác khi bar không phải là một cuộc gọi.

Nếu nhiều số foo() là phổ biến cho cả hai loại lời gọi (có thể có nhiều tác dụng phụ, và bar() được cung cấp dưới dạng gọi lại?), Thì bạn có thể điều kiện phần tùy chọn bằng cách sử dụng std::is_same<>. Điều này đòi hỏi if constexpr, như lambda là không thể được gọi là bar(3):

static auto const NULL_LAMBDA = nullptr; 

#include <type_traits> 
template <typename Lambda> 
int foo(Lambda bar) { 
    if constexpr (std::is_same<decltype(bar), std::nullptr_t>::value) 
     return -1; 
    else 
     return bar(3); 
} 

#include <iostream> 
int main() { 
    std::cout << foo([](int a) {return a + 3;}) << std::endl; 
    std::cout << foo(NULL_LAMBDA) << std::endl; 
} 
+2

Lambdas không hỗ trợ '=='. Trừ khi có cơ hội cả hai bên là lambdas vô nghĩa với cùng một chữ ký và bạn không sử dụng MSVC. –

+2

Tôi sẽ không đề cập đến '== 'ở tất cả; đó là một con giun khổng lồ. Nó chỉ biên dịch bởi vì hàm chuyển đổi thành con trỏ hàm (đòi hỏi cả hai mặt phải được giữ lại).Và vì mọi biểu thức lambda tạo ra một kiểu mới không thể tưởng tượng được, nó cực kỳ giòn: 'foo ([] (int) {return 0;})' có thể hoặc không thể nhấn vào đường dẫn 'NULL_LAMBDA'. –

2

Lambdas là một chủng loại các loại, không phải là một kiểu.

Chúng ta có thể làm điều này:

struct null_callable_t{ 
    template<class...Ts> 
    constexpr void operator()(Ts&&...)const{} 
    explicit constexpr operator bool()const{return false;} 
    constexpr null_callable_t() {} 
    friend constexpr bool operator==(::std::nullptr_t, null_callable_t){ return true; } 
    friend constexpr bool operator==(null_callable_t, ::std::nullptr_t){ return true; } 
    friend constexpr bool operator!=(::std::nullptr_t, null_callable_t){ return false; } 
    friend constexpr bool operator!=(null_callable_t, ::std::nullptr_t){ return false; } 
}; 

constexpr null_callable_t null_callable{}; 

Bây giờ mã của chúng tôi trở thành:

template <typename Lambda> 
int foo(Lambda bar) { 
    if(!bar) 
    return -1; 
    else 
    return bar(3); 
} 

mà là khá trơn:

std::cout << foo([](int a) {return a + 3;}) << std::endl; 
std::cout << foo(null_callable) << std::endl; 
Các vấn đề liên quan