Подтвердить что ты не робот

Как я могу использовать 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, а проверка условия передала лямбда-функцию, является ли она нулевой?

4b9b3361

Ответ 1

Вы можете добавить специальную специализацию:

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

Ответ 2

В этом конкретном случае вы можете просто определить нулевое замыкание, которое всегда возвращает -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() предоставляется как обратный вызов?), тогда вы можете условно выделить дополнительную часть, используя 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;
}

Ответ 3

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;