Проверьте, нет ли лямбда?

Как бы я пошел на тестирование, если лямбда не имеет апатридов, то есть, если она что-то захватывает или нет? Мое предположение было бы использовать разрешение перегрузки с перегрузкой указателя на функцию или специализацию шаблона?

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/...