【问题标题】:Test if a lambda is stateless?测试 lambda 是否是无状态的?
【发布时间】:2013-11-26 12:46:00
【问题描述】:

如果 lambda 是无状态的,即它是否捕获任何内容,我将如何进行测试? 我的猜测是使用带有函数指针重载或模板特化的重载解析?

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

【问题讨论】:

  • 提示:如果可转换为函数指针,则为无状态。
  • @Nawaz:很有趣。你有任何来自标准的报价吗?
  • @MM.:我不记得任何引用,但如果 lambda 没有捕获任何变量,那么它可以隐式转换为函数指针。
  • 就我个人而言,我想知道为什么它会很重要,以及为什么有人想要单独列出 lambda。我可怜的旧函数对象感觉被忽视了。
  • @R.MartinhoFernandes:因为无状态 lambda 转换为精确的一种函数指针类型,并且调用结果指针也一样?在一般函子情况下没有保证。

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


【解决方案1】:

根据标准,如果 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.

【讨论】:

  • 请注意这里缺少一个重要的测试:即is_lambda(考虑:struct poor_bastard { void operator()() const; operator void(*)(void*)() const; int haha_i_have_state_shut_up; };)。另请注意,这将无法与即将到来的多态 lambda 一起使用。
  • @Skeen:为什么?这个特定的信息是普遍感兴趣的吗? (不!)将它放在标准中与在需要它的(相当罕见的)场合如上实施它会得到什么吗? (再次,不!)我认为 Nawaz 在满足 OP 的要求方面做得很好,但我认为在标准中加入类似的东西没有任何好处。
  • IMO 一个同时产生误报和误报的测试毫无价值。
  • 没有捕获的 lambda 不一定是无状态的顺便说一句,这使得这个测试的结果比现在更没有价值。 (考虑:static int where_is_your_statelessness_now = 0; void bar() { auto f = [/* look ma, no captures! */]{ return ++where_is_your_statelessness_now; }; ... }
  • 仅仅因为 lambda 是无状态的,并不意味着它可以转换为指向函数的指针 - 考虑 auto L = []() { return [=] { return 1; }; };自动无状态 = L();但是 stateless 不会转换为 fptr。只有没有任何显式、默认或初始化捕获的 lambda 可以转换为 fptr。
【解决方案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) );

不适用于多态 lambda,前提条件是参数是闭包类型。 (例如,is_captureless_lambda&lt;int&gt;std::true_type。)

【讨论】:

    【解决方案3】:

    根据 § 5.1.2/6

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

    【讨论】:

    • 可能会提到使用std::is_convertible 来实际执行检查。
    • 标准是否要求捕获 lambda 必须转换为函数指针?也许一个捕获的 lambda 可以转换为一个函数指针,该指针需要一个指向对象状态的额外指针(如“this”)?
    • @MM.:请注意,您的解决方案不是通用的。您假设函数指针的签名。如果签名不匹配,并不一定意味着 lambda 不是无状态的,也可能意味着您一开始就使用了错误的签名。
    • 也仅仅因为 lambda 没有捕获任何东西,并不意味着它具有到函数指针的转换。如果您编写默认捕获并且即使 lambda 实际上没有捕获任何内容 - 它也不会转换为 fptr。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      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查看我的其他答案:https://stackoverflow.com/a/34873353/225186

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-11
        • 1970-01-01
        • 2023-03-30
        • 1970-01-01
        • 2017-05-31
        • 1970-01-01
        • 2011-04-09
        相关资源
        最近更新 更多