c – 使用可变参数模板参数来解析lambda签名

c – 使用可变参数模板参数来解析lambda签名,第1张

概述虽然有很多东西浮出水面来获取任何模板化回调函数/方法的返回类型(当然包括lambdas),但我很难找到有关解析lambda函数的完整调用签名的信息.至少在 gcc 4.7中,似乎是一个边缘情况,正常的技巧(见下文)不起作用.这是我到目前为止所做的事情(当然是一个精简版)…… template<typename Sig>struct invokable_type { };template<ty 虽然有很多东西浮出水面来获取任何模板化回调函数/方法的返回类型(当然包括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的类型是类类型,就像“普通的,命名的函数对象类型”一样:

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 declared const (9.3.1) if and only if the lambda-Expression’s parameter-declaration-clause is not followed by mutable. It is neither virtual nor declared
volatile. […]

(然后通过指定属性和异常规范继续)

这意味着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签名所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1215460.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-05
下一篇 2022-06-05

发表评论

登录后才能评论

评论列表(0条)

保存