提问人:thor 提问时间:2/9/2014 更新时间:3/21/2018 访问量:18743
C++11/1Y Lambda 函数的类型签名是什么?
what is the type signature of a c++11/1y lambda function?
问:
我想知道是否有一种标准方法可以获取任何给定 lambda 参数的类型签名(即返回类型和类型)?
我问的原因是我一直想知道声明中的类型到底是什么。在其他用例中,它是较长类型名称的一种方便且较短的替代方法。但是对于 lambda,有没有其他方法可以声明 lambda 变量?auto
auto l =[](int x,int y)->int{return x+y;}
auto
我的理解是,标准 lambda 只不过是一个函数对象,它是它自己的类型。因此,即使两个 lambda 具有相同的返回类型和参数类型,它们仍然是两个不同的、不相关的类/函子。但是,有没有办法捕捉到它们在类型签名方面相同的事实?
我认为我正在寻找的类型签名可以类似于正确类型的对象。std::function<>
一个更有用/涉及的问题是,如果可以提取类型签名,则可以编写一个通用包装函数来将任何 lambda 函数转换为相同类型签名的对象。std::function
答:
你是对的,C++11 lambda 的类型是匿名的和实例唯一的。
该类型可以存储对我遇到的任何类型的 lambda 的引用,但据说性能受到影响。std::function
尝试
std::function<int (int, int)> f = [](int x, int y) -> int {
return x + y;
};
请注意,在诸如此类的非歧义方案中可以省略 。-> int
C++14 让我们写
std::function<int (int, int)> f = [](auto x, auto y) {
return x + y;
};
这对于长类型名称很方便。
正如 @Jonathan Wakely 所指出的,这种方法使用带有固定模板参数的 std::function 捕获特定的实例化。在 C++14 中,可以指定模板变量。此外,同样根据 C++14,lambda 参数可以通过 推断其类型,允许以下内容:auto
template<class T>
std::function<T (T, T)> g = [](auto x, auto y) -> auto {
return x + y;
};
目前,VC++ 和 GCC 似乎不支持函数级别的变量声明模板,但允许它们在成员、命名空间和全局声明中使用。我不确定此限制是否来自规范。
注意:我不使用 clang。
评论
std::function
int(int,int)
[](auto... x) { }
std::function
template<class T> std::function< T (T, T) > f = (auto x,auto y) {return x+y;};
在 C++1y 中,有通用的 lambda,没有单个调用签名( 是模板)。operator()()
评论
operator()()
我想知道是否有一种标准方法可以获取任何给定 lambda 参数的类型签名(即返回类型和类型)?
不,没有。
我问的原因是我一直想知道声明中的类型到底是什么。
auto
auto l =[](int x,int y)->int{return x+y;}
它是由实现创建的未指定的类类型。lambda 的全部意义在于它们是“匿名函数”,即您不知道它们的类型。
如果需要已知类型,请编写函数对象类型。
在 auto 的其他用例中,它是较长类型名称的一种方便且较短的替代方法。但是对于 lambda,有没有其他方法可以声明 lambda 变量?
不。
如果要自己声明类型,请不要使用 lambda 表达式。
我的理解是,标准 lambda 只不过是一个函数对象,它是它自己的类型。因此,即使两个 lambda 具有相同的返回类型和参数类型,它们仍然是两个不同的、不相关的类/函子。
正确。每个 lamda 表达式都会生成一个唯一的类型。
但是,有没有办法捕捉到它们在类型签名方面相同的事实?
不,没有语言功能允许这样做。
我认为我正在寻找的类型签名可以是正确类型的 std::function<>对象。
即使在 C++11 中是可能的,在 C++14 中也无济于事,因为 lambda 表达式可以接受任何数字和任何类型的参数,例如[](auto... a) { }
无论如何,如果您不知道 lambda 函数的调用签名,那么我会说您错误地使用了 lambda。当您编写 lambda 时,您应该知道它的属性是什么,因此,当您知道它的属性时,请立即使用它,或者尽早将其放入(或捕获其调用签名的其他类型)中。如果您正在创建 lambda 并在不知道调用签名的情况下非本地使用它们,那么您就做错了。std::function
评论
std::function<void(int)> f( [](long) { return true; } );
bool(long)
function<void(int)>
function<void(int)> f2( [](auto...) { return 11; } )
std::function
std::function
std::function
std::function
根据 Can the 'type' of a lambda expression be expressed?,在当前的 C++ 中实际上有一种简单的方法(不需要 c++1y)来计算 lambda 的return_type和参数类型。适应这一点,为每个 lambda 组装一个类型化签名类型(如下所述)并不困难。std::function
f_type
I. 有了这个抽象类型,实际上可以有另一种方法来表达 lambda 的类型签名,如下所示。注意:它不是 lambda 的真实类型,而是 lambda 类型签名的函数摘要。然而,它可能比真正的 lambda 类型更有用,因为每个 lambda 都是它自己的类型。auto
function_traits<..>::f_type
f_type
如下代码所示,就像一个人可以使用一样,也可以做,这是神秘的另一种选择。当然,这种相似性只是形式上的。下面的代码涉及将 lambda 转换为 a,在构造对象时需要类型擦除的成本,以及通过对象进行间接调用的少量成本。但是,撇开这些实现问题不谈(我不认为这是根本性的,应该永远存在),毕竟,显式表达任何给定 lambda 的(抽象)类型签名是可能的。vector<int>::iterator_type i = v.begin()
function_traits<lambda>::f_type f = lambda
auto
std::function
std::function
std::function
std::function
II. 也可以编写一个包装器(非常类似于 和 )来自动将 lambda(和其他可调用对象,如函数指针/函子)转换为 ,具有相同的类型演绎功能。make_function
std::make_pair
std::make_tuple
f
std::function
测试代码如下:
#include <cstdlib>
#include <tuple>
#include <functional>
#include <iostream>
using namespace std;
// For generic types that are functors, delegate to its 'operator()'
template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
//enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) > {
typedef function<ReturnType (Args...)> f_type;
};
// for function pointers
template <typename ReturnType, typename... Args>
struct function_traits<ReturnType (*)(Args...)> {
typedef function<ReturnType (Args...)> f_type;
};
template <typename L>
typename function_traits<L>::f_type make_function(L l){
return (typename function_traits<L>::f_type)(l);
}
long times10(int i) { return long(i*10); }
struct X {
double operator () (float f, double d) { return d*f; }
};
// test code
int main()
{
auto lambda = [](int i) { return long(i*10); };
typedef function_traits<decltype(lambda)> traits;
traits::f_type ff = lambda;
cout << make_function([](int i) { return long(i*10); })(2) << ", " << make_function(times10)(2) << ", " << ff(2) << endl;
cout << make_function(X{})(2,3.0) << endl;
return 0;
}
评论