Test if a lambda is stateless?

后端 未结 5 1431
春和景丽
春和景丽 2021-02-07 12:38

How would I go about testing if a lambda is stateless, that is, if it captures anything or not? My guess would be using overload resolution with a function pointer overload, or

相关标签:
5条回答
  • 2021-02-07 13:17

    Boost.TypeTraits is_stateless seems to do the job for whatever reason without much drama:

    #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 is simple the combination of other conditions, it can be expressed in terms of standard type traits I suppose:

    ::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

    Check my other answer based on sizeof: https://stackoverflow.com/a/34873353/225186

    0 讨论(0)
  • 2021-02-07 13:18

    As per the Standard, if a lambda doesn't capture any variable, then it is implicitly convertible to function pointer.

    Based on that, I came up with is_stateless<> meta-function which tells you whether a lambda is stateless or not.

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

    And here is the test code:

    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";
    }
    

    Output:

    false
    true
    false
    

    Online Demo.

    0 讨论(0)
  • 2021-02-07 13:23
    #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) );
    

    Does not work for polymorphic lambdas, require as a precondition that the argument be a closure type. (E.g. is_captureless_lambda<int> is std::true_type.)

    0 讨论(0)
  • 2021-02-07 13:23

    Per § 5.1.2/6

    The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function with C ++ language linkage (7.5) having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator. For a generic lambda with no lambda-capture, the closure type has a public non-virtual non-explicit const conversion function template to pointer to function.

    If it's convertible to a pointer to function, then MAYBE it has to not capture anything (stateless). In action:

    int v = 1;
    auto lambda1 = [ ]()->void {};
    auto lambda2 = [v]()->void {};
    
    using ftype = void(*)();
    
    ftype x = lambda1; // OK
    ftype y = lambda2; // Error
    

    You can also use std::is_convertible:

    static_assert(is_convertible<decltype(lambda1), ftype>::value, "no capture");
    static_assert(is_convertible<decltype(lambda2), ftype>::value, "by capture");
    
    0 讨论(0)
  • 2021-02-07 13:23

    An option could be to explicitly look a the size of the type, a stateless should in principle have the same size as other stateless types (I picked std::true_type for a reference 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 );
    }
    

    I don't know how portable this solution is. In any case check my other solution: https://stackoverflow.com/a/34873139/225186

    0 讨论(0)
提交回复
热议问题