2017-08-17 106 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;