gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 20:13:52 26 4
gpt4 key购买 nike

虽然有很多关于获取任何模板化回调函数/方法(当然包括 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...))存在专门化(以及进一步的属性/typedef) , 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 编译为定义范围的“ namespace ”内的静态函数(gcc 似乎确实如此)。对于我的生活,我无法弄清楚签名实际上是什么。看起来它肯定有一个应该可以通过模板特化(基于错误报告)推导出来的。

另一个切线问题是,即使有我可以使用的签名,交叉编译器有多危险? lambda 编译签名是标准化的还是全面的?

最佳答案

从评论中总结和延伸:

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

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...)>;

应该已经能够处理 lambda 表达式了。商会

#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's version然而更一般,可能更好,依靠 SFINAE 检查是否存在 T::operator() ,如果找不到运算符(operator),它可以提供更好的诊断消息。

注意如果您使用一元 + 为不捕获任何内容的 lambda 表达式添加前缀,它将被转换为指向函数的指针。 do_something( +[](int){} )将与专业合作 invokable_type<Return(*)(Args...)> .

关于c++ - 使用可变模板参数解析 lambda 签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19149150/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com