加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-16 09:47:50 所属栏目:百科 来源:网络整理
导读:虽然有很多东西浮出水面来获取任何模板化回调函数/方法的返回类型(当然包括lambdas),但我很难找到有关解析lambda函数的完整调用签名的信息.至少在 gcc 4.7中,似乎是一个边缘情况,正常的技巧(见下文)不起作用.这是我到目前为止所做的事情(当然是一个精简版)…
虽然有很多东西浮出水面来获取任何模板化回调函数/方法的返回类型(当然包括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 ...)>.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读