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