c – 使用可变参数模板参数来解析lambda签名
虽然有很多东西浮出水面来获取任何模板化回调函数/方法的返回类型(当然包括lambdas),但我很难找到有关解析lambda函数的完整调用签名的信息.至少在
gcc 4.7中,似乎是一个边缘情况,正常的技巧(见下文)不起作用.这是我到目前为止所做的事情(当然是一个精简版)……
template<typename Sig> struct invokable_type { }; template<typename R,typename...As> struct invokable_type<R(As...)> { static constexpr size_t n = sizeof...(As); typedef R(callable_type)(As...); template<size_t i> struct arg { typedef typename peel_type<i,As...> type; }; }; peel_type< size_t,typename ...>为了简洁起见,这里不包括它,但它是一个简单的参数类型削皮器(我认为有一个内置于C 11,但我从来没有费心去看).这个问题并不重要. 然后,当然,对于无数可调用类型存在特化(以及其他属性/ typedef),例如R(*)(As …),R(&)(As …),(R(T: :*)(As …),std :: function< R(As ...)>,方法cv限定符,方法lvalue / rvalue限定符等,等等. 然后,在路上的某个地方,我们有一个可爱的功能或方法(功能在这里,无关紧要),看起来像…… template<typename C,typename...As> static void do_something(C&& callback,As&&...as) { do_something_handler<invokable_type<C>::n,As...>::something(std::forward<C>(callback),std::forward<As>(as)...); } 别担心do_something_handler会做什么……这完全不重要.问题在于lambda函数. 对于我专门用于的所有可能的通用可调用签名(看起来只是非STL仿函数),当使用它们作为第一个参数(模板推导完全有效)调用do_something()时,这非常有效.然而,lambda函数导致未捕获的类型签名,导致invokable_type< Sig>.正在使用,这意味着像:: n和:: args< 0> :: type这样的东西根本不存在. 不是问题的例子…… void something(int x,int y) { return x * y; } … 然后… do_something(something,7,23); 问题例子…… do_something([](int x,int y) { return x * y; },23); 如果我正确理解lambda函数,编译器很可能将此lambda编译为定义范围的“命名空间”内的静态函数(gcc当然可以).对于我的生活,我无法弄清楚签名究竟是什么.它看起来肯定有一个应该通过模板特化(基于错误报告)可以推导出来. 另一个切线问题是,即使有我可以使用的签名,交叉编译器如何危险这个呢? lambda编译签名是标准化的还是全面的? 解决方法
总结并从评论中扩展:
Per [expr.prim.lambda] / 3,lambda-expression的类型是类类型,就像“普通的,命名的函数对象类型”一样:
再往下,/ 5指定:
(然后通过指定属性和异常规范继续) 这意味着lambda [](int p){return p / 2.0;在这方面表现得很像 struct named_function_object { double operator() (int p) const { return p/2.0; } }; 因此,你的第一次专业化 template<typename R,typename...As> struct invokable_type<R(As...)>; 应该已经能够处理lambdas了. SSCCE #include <utility> template<class T> struct decompose; template<class Ret,class T,class... Args> struct decompose<Ret(T::*)(Args...) const> { constexpr static int n = sizeof...(Args); }; template<class T> int deduce(T t) { return decompose<decltype(&T::operator())>::n; } struct test { void operator() (int) const {} }; #include <iostream> int main() { std::cout << deduce(test{}) << std::endl; std::cout << deduce([](int){}) << std::endl; } 在最新版本的clang和g上编译好.似乎问题与g 4.7有关 进一步的研究表明,g -4.7.3编译了上述例子. 问题可能与lambda表达式产生函数类型的误解有关.如果我们将do_something定义为 template<class C> void do_something(C&&) { std::cout << invokable_type<C>::n << std::endl; } 然后对于像do_something([](int){})这样的调用,模板参数C将被推导为闭包类型(无引用),即类类型.上面定义的结构测试的类似情况是do_something(test {}),在这种情况下,C将被推导出来进行测试. 因此,实例化的invokable_type的特化是一般情况 template<class T> struct invokable_type; 因为在这两种情况下T都不是像指针或函数类型那样的“复合类型”.这种一般情况可以通过假设它只采用纯类类型,然后使用该类类型的成员T :: operator()来使用: template<class T> struct invokable_type { constexpr static int n = invokable_type<&T::operator()>::n; }; 或者,正如Potatoswatter所说,通过继承 template<class T> struct invokable_type : invokable_type<&T::operator()> {}; 然而,Potatoswatter’s version更通用,可能更好,依赖于SFINAE检查T :: operator()的存在,如果找不到运算符,它可以提供更好的诊断消息. 注:如果为lambda表达式添加前缀,该表达式不会捕获带有一元的任何内容,则它将转换为指向函数的指针. do_something([](int){})将使用特殊化invokable_type< Return(*)(Args ...)>. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |