Проверьте, нет ли лямбда?
Как бы я пошел на тестирование, если лямбда не имеет апатридов, то есть, если она что-то захватывает или нет?
Мое предположение было бы использовать разрешение перегрузки с перегрузкой указателя на функцию или специализацию шаблона?
int a;
auto l1 = [a](){ return 1; };
auto l2 = [](){ return 2; };
// test l1 and l2, get a bool for statelessness.
Ответы
Ответ 1
В соответствии со стандартом, если лямбда не фиксирует какую-либо переменную, то она неявно конвертируется в указатель функции.
Исходя из этого, я придумал мета-функцию is_stateless<>
, которая сообщает вам, является ли лямбда апатридом или нет.
#include <type_traits>
template <typename T, typename U>
struct helper : helper<T, decltype(&U::operator())>
{};
template <typename T, typename C, typename R, typename... A>
struct helper<T, R(C::*)(A...) const>
{
static const bool value = std::is_convertible<T, R(*)(A...)>::value;
};
template<typename T>
struct is_stateless
{
static const bool value = helper<T,T>::value;
};
И вот тестовый код:
int main()
{
int a;
auto l1 = [a](){ return 1; };
auto l2 = [](){ return 2; };
auto l3 = [&a](){ return 2; };
std::cout<<std::boolalpha<<is_stateless<decltype(l1)>::value<< "\n";
std::cout<<std::boolalpha<<is_stateless<decltype(l2)>::value<< "\n";
std::cout<<std::boolalpha<<is_stateless<decltype(l3)>::value<< "\n";
}
Вывод:
false
true
false
Онлайн-демонстрация.
Ответ 2
#include <type_traits> // std::true_type, std::false_type
#include <utility> // std::declval
template<typename Lambda>
auto is_captureless_lambda_tester(int)
-> decltype( +std::declval<Lambda>(), void(), std::true_type {} );
template<typename Lambda>
auto is_captureless_lambda_tester(long)
-> std::false_type;
template<typename Lambda>
using is_captureless_lambda = decltype( is_captureless_lambda_tester<Lambda>(0) );
Не работает для полиморфных лямбда, требуется в качестве предварительного условия, чтобы аргумент был типом замыкания. (Например, is_captureless_lambda<int>
- std::true_type
.)
Ответ 3
В § 5.1.2/6
Тип замыкания для не-общего лямбда-выражения без лямбда-захвата имеет публичную не виртуальную неявную функцию преобразования const для указателя на функцию с С++ языковой связью (7.5), имеющей тот же параметр и возвращающий типа в качестве оператора вызова функции закрытия. Значение, возвращаемое этой функцией преобразования, должно быть адресом функции, которая при вызове имеет тот же эффект, что и вызов оператора вызова типа закрытия. Для общей лямбда без лямбда-захвата тип замыкания имеет открытый не виртуальный неявный const-шаблон преобразования const для указателя на функцию.
Если он конвертируется в указатель на функцию, то MAYBE он не должен ничего записывать (без гражданства). В действии:
int v = 1;
auto lambda1 = [ ]()->void {};
auto lambda2 = [v]()->void {};
using ftype = void(*)();
ftype x = lambda1; // OK
ftype y = lambda2; // Error
Вы также можете использовать std::is_convertible
:
static_assert(is_convertible<decltype(lambda1), ftype>::value, "no capture");
static_assert(is_convertible<decltype(lambda2), ftype>::value, "by capture");
Ответ 4
Boost.TypeTraits is_stateless
кажется, делает работу по любой причине без большой драмы:
#include<boost/type_traits.hpp>
#include<cassert>
int main(){
auto l1 = [a](){ return 1; };
auto l2 = [](){ return 2; };
auto l3 = [&a](){ return 2; };
assert( boost::is_stateless<decltype(l1)>::value == false );
assert( boost::is_stateless<decltype(l2)>::value == true );
assert( boost::is_stateless<decltype(l3)>::value == false );
}
boost::is_stateless
является простой комбинацией других условий, ее можно выразить в терминах стандартных типов. Я полагаю:
::boost::is_stateless =
::boost::has_trivial_constructor<T>::value
&& ::boost::has_trivial_copy<T>::value
&& ::boost::has_trivial_destructor<T>::value
&& ::boost::is_class<T>::value
&& ::boost::is_empty<T>::value
http://www.boost.org/doc/libs/1_60_0/libs/type_traits/doc/html/boost_typetraits/reference/is_stateless.html
Проверьте мой другой ответ на основе sizeof
: fooobar.com/questions/447565/...
Ответ 5
Параметр может состоять в том, чтобы явно посмотреть размер этого типа, в принципе не имеющее аналогов должно иметь тот же размер, что и другие типы без состояния (я выбрал std::true_type
для ссылочного типа).
#include<cassert>
#include<type_traits>
template<class T>
struct is_stateless_lambda : std::integral_constant<bool, sizeof(T) == sizeof(std::true_type)>{};
int main(){
auto l1 = [a](){ return 1; };
auto l2 = [](){ return 2; };
auto l3 = [&a](){ return 2; };
assert( boost::is_stateless_lambda<decltype(l1)>::value == false );
assert( boost::is_stateless_lambda<decltype(l2)>::value == true );
assert( boost::is_stateless_lambda<decltype(l3)>::value == false );
}
Я не знаю, насколько переносимо это решение. В любом случае проверьте мое другое решение: fooobar.com/info/447565/...