2017-08-17 48 views
17

我要聲明這樣的功能:如何在C++中使用Null Lambda?

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; 
} 

然後,我怎麼可以聲明NULL_LAMBDA和狀態檢查通過lambda函數是否爲空?

+19

你所說的 「空拉姆達」 是什麼意思? – melpomene

+1

你可以使用'std :: optional'或其他庫中的等價物嗎? – KABoissonneault

回答

26

您可以添加專用的專業化:

#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

模板專業化與使用'nullptr_t'的非模板重載是否有利? – LWimsey

+3

@LWimsey具有模板特化功能可以在明確提供模板參數時調用正確的'foo'。 – VTT

+1

請不要在這裏使用專業化。它只是增加了一個正常的過載,並且只會導致問題,如果你添加一個不同的過載。 – Barry

8

在這種特殊情況下,你可以定義空封閉物,一個總是返回-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; 
} 

的可能性是,如果你在運行時上選擇哪個實現可以通過,那麼最好用std::function刪除它,而不是實例化模板。並且std::function被允許爲 - 它可以從空指針分配並與其進行比較。


如果你知道在編譯時,一些通話網站總是會通過「零」拉姆達,那麼你就可以適當地專門的實施。明顯的選項包括將foo()與不參與bar參數的版本重載,或者在bar不可調用時使用不同的實現進行專門化。

如果太多的foo()是常見的兩種類型的調用(也許它有很多的副作用,bar()提供的回調?),那麼你可能能夠使用conditionalise的std::is_same<>可選部分。這需要if constexpr,因爲拉姆達不調用作爲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不支持'=='。除非偶然,雙方都是具有相同簽名的捕獲型lambda,而您沒有使用MSVC。 –

+2

我不會提到'==';這是一個巨大的蠕蟲罐。它只是因爲函數指針的轉換函數而編譯(這要求雙方都是無捕獲的)。由於每個lambda表達式都會創建一個新的不可分類的類型,它非常脆弱:'foo([](int){return 0;})'可能會或可能不會到達'NULL_LAMBDA'路徑。 –

2

lambda表達式是一個類型的類別,而不是一種類型。

我們可以這樣做:

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{}; 

現在我們的代碼變成:

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

這是非常漂亮:

std::cout << foo([](int a) {return a + 3;}) << std::endl; 
std::cout << foo(null_callable) << std::endl; 
相關問題