2017-08-17 5 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を宣言することができ、条件はnullであるかどうかを渡されたラムダ関数をチェック!

+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空のであることが許されています - それはヌルポインタから割り当てられ、それと比較することができます。


あなたには、いくつかのコールサイトが常に「NULL」ラムダに合格することをコンパイル時でを知っている場合は、適切な実装を特化することができます。明らかなオプションには、bar引数を取らないバージョン、またはbarが呼び出し可能でない場合に異なる実装でそれを特化するバージョンでfoo()にオーバーロードすることが含まれます。

foo()の多くは(おそらくそれは、副作用をたくさん持っている、とbar()がコールバックとして提供されている?)呼び出しの両方の種類に共通している場合は、std::is_same<>を使用して、オプションパーツをconditionaliseことができるかもしれません。ラムダはbar(3)として呼び出すことはできませんので、これは、if constexprが必要です。

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

Lambdaは '=='をサポートしていません。偶然に両側が同じシグネチャーのキャプチャレスラムダで、あなたがMSVCを使用していない場合を除きます。 –

+2

私は '=='を全く言及しません。それはワームの巨大な缶です。これは、関数ポインタへの変換関数のためにのみコンパイルされます(両方の側がキャプチャされている必要があります)。foo([](int){return 0;}) 'は、' NULL_LAMBDA'パスにヒットすることもあれば、ヒットしないこともあります。 –

2

ラムダは、種類のカテゴリ、ないタイプです。

我々はこれを行うことができます:

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; 
関連する問題