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的类型是类类型,就像“普通的,命名的函数对象类型”一样:
The type of the lambda-Expression (which is also the type of the closure object) is a unique,unnamed non-union class type — called the closure type […]
再往下,/ 5指定:
The closure type for a lambda-Expression has a public
inline
function call operator (13.5.4) whose parameters and return type are described by the lambda-Expression’s parameter-declaration-clause and trailing-return-type respectively. This function call operator is declaredconst
(9.3.1) if and only if the lambda-Expression’s parameter-declaration-clause is not followed by mutable. It is neither virtual nor declaredvolatile
. […]
(然后通过指定属性和异常规范继续)
这意味着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 ...)>.
总结以上是内存溢出为你收集整理的c – 使用可变参数模板参数来解析lambda签名全部内容,希望文章能够帮你解决c – 使用可变参数模板参数来解析lambda签名所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)