如何在C ++中使用Null Lambda?

时间:2017-08-17 06:23:44

标签: c++ c++11 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函数是否为null?

4 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:8)

在这种特殊情况下,您可以将null闭包定义为始终返回-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被允许为空 - 它可以从空指针中分配和比较。

如果您在编译时知道 某些呼叫网站将始终通过&#39; null&#39; lambda,那么你可以适当地专门化实现。显而易见的选项包括使用不会使用foo()参数的版本重载bar,或者在bar不可调用时将其专门化为不同的实现。

如果foo()的大部分都适用于这两种调用(可能它有很多副作用,并且bar()是作为回调提供的?),那么你可以将条件化使用std::is_same<>的可选部分。这需要if constexpr,因为lambda不能被调用为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 :(得分:2)

Lambdas是一类类型,而不是类型。

我们可以这样做:

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;

答案 3 :(得分:0)

注意在 C++17 中我们可以这样写......

template<typename Lambda>
int foo(Lambda bar)
{
    if constexpr (std::is_same_v<std::decay_t<Lambda>, std::nullptr_t>)
        return -1;
    else if constexpr (std::is_convertable_v<Lambda, bool>)
    {
        if (bar)
            return bar(3);
        else
            return -1;
    }
    else
        return bar(3);
}

我想在 C++20 中我们可以定义一个概念,std::invocable(概念头)或 nulltpr_t 来约束 Lambda。