测试lambda是否是无状态的?

时间:2013-11-13 18:44:59

标签: c++ c++11 lambda c++14

如果lambda是无状态的,也就是说,如果它捕获了任何东西,我将如何测试? 我的猜测是使用带有函数指针重载或模板特化的重载解析?

int a;
auto l1 = [a](){ return 1; };
auto l2 = [](){ return 2; };
// test l1 and l2, get a bool for statelessness.

5 个答案:

答案 0 :(得分:14)

根据标准,如果lambda没有捕获任何变量,那么它可以隐式转换为函数指针。

基于此,我提出了is_stateless<>元函数,它告诉你lambda是否无状态

#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

Online Demo

答案 1 :(得分:6)

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

对多态lambda无效,需要作为参数为闭包类型的前提条件。 (例如is_captureless_lambda<int>std::true_type。)

答案 2 :(得分:5)

Per§5.1.2/ 6

  

没有lambda-capture的非泛型lambda表达式的闭包类型有一个公共的非虚拟非显式const转换函数指向函数,C ++语言链接(7.5)具有相同的参数并返回类型作为闭包类型的函数调用操作符。此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果。对于没有lambda-capture的通用lambda,闭包类型有一个公共的非虚拟非显式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");

答案 3 :(得分:0)

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

根据sizeofhttps://stackoverflow.com/a/34873353/225186

检查我的其他答案

答案 4 :(得分:0)

一个选项可以明确地查看类型的大小,无状态原则上应该与其他无状态类型具有相同的大小(我选择了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 );
}

我不知道这个解决方案有多便携。在任何情况下,请检查我的其他解决方案:https://stackoverflow.com/a/34873139/225186