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

斯托布先生

尽管有很多东西可以获取任何模板化回调函数/方法的返回类型(当然包括lambda),但我很难找到有关解决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内置了一个削皮器,但我从不费力看一下)。这个问题无关紧要。

然后,当然,特(和进一步的性质/类型定义),用于可调用类型如无数存在R(*)(As...)R(&)(As...)(R(T::*)(As...)std::function<R(As...)>,方法CV限定符,方法左值/右值限定符,等等等等等等

然后,在路上的某个地方,我们有一个漂亮的函数或方法(这里的函数无关紧要),看起来像...

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;
    }, 7, 23);

如果我正确理解了lambda函数,则编译器很可能将此lambda编译为定义范围的“名称空间”内的静态函数(gcc当然可以)。对于我的一生,我无法弄清楚签名实际上是什么。看起来它肯定有一个应该通过模板专门化(基于错误报告)来推导的。

另一个切线问题是,即使有我可以使用的签名,这对交叉编译器有多危险呢?Lambda编译签名是否已标准化?

总结并扩展评论:

按照[expr.prim.lambda] / 3,lambda表达式的类型是类类型,就像“普通的,命名的函数对象类型”一样:

的类型的λ-表达(这也是封闭的对象的类型)是一个独特的,无名不愈合类类型-称为闭合类型[...]

再往下,/ 5指定:

lambda表达式的闭包类型有一个公共inline函数调用运算符(13.5.4),其参数和返回类型分别由lambda-expressionparameter-declaration-clauseTrailing-return-type来描述const当且仅当lambda-expression的parameter-declaration-clause后没有可变变量时,此函数调用运算符才声明为(9.3.1)它既不是虚拟的也不是声明的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...)>;

应该已经能够处理lambda。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将被推导为闭包类型(无引用),即类类型。struct test上面定义的类似情况将是do_something( test{} ),在这种情况下C将推导为test

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的版本更通用,并且可能更好,它依赖于SFINAE检查是否存在T::operator(),如果找不到操作员,它可以提供更好的诊断消息。

注意:如果在lambda表达式前添加了不能捕获任何一元字符的lambda表达式+,则会将其转换为函数指针。do_something( +[](int){} )将专业化invokable_type<Return(*)(Args...)>

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

可变参数模板函数接受lambda

来自分类Dev

可变参数模板函数接受lambda

来自分类Dev

可变参数模板对

来自分类Dev

使用可变参数模板的工厂模式?

来自分类Dev

使用可变参数模板扩展

来自分类Dev

使用可变参数模板打印列表

来自分类Dev

在可变参数模板中使用对

来自分类Dev

С++可变参数模板:实现可变参数

来自分类Dev

可变参数模板类的可变参数模板

来自分类Dev

可变参数模板类的可变参数模板

来自分类Dev

可变参数模板示例

来自分类Dev

可变参数模板提取

来自分类Dev

可变参数模板示例

来自分类Dev

可变参数模板的论点

来自分类Dev

在C ++中使用可变参数模板迭代参数包

来自分类Dev

使用可变参数模板参数提高变体访客

来自分类Dev

是否可以从函数模板返回可变参数的lambda?

来自分类Dev

是否可以从函数模板返回可变参数的lambda?

来自分类Dev

可变参数函数模板的怪异重载解析

来自分类Dev

使用可变参数lambda迭代可变参数功能模板的参数

来自分类Dev

clang不使用可变参数推断可变参数模板函数中的模板参数

来自分类Dev

我还需要使用可变参数模板继承来创建lambda重载?

来自分类Dev

使用带有可变参数模板类的boost mpl lambda

来自分类常见问题

如何存储可变参数模板参数?

来自分类Dev

用作可变参数模板的参数

来自分类Dev

可变参数模板参数和策略

来自分类Dev

可变参数模板参数的元迭代

来自分类Dev

C ++可变参数模板参数迭代

来自分类Dev

std :: function的可变参数模板参数