可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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 template specialization?
int a; auto l1 = [a](){ return 1; }; auto l2 = [](){ return 2; }; // test l1 and l2, get a bool for statelessness.
回答1:
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.
回答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) );
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
.)
回答3:
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");
回答4:
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
回答5:
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