什么是c ++ 11 / 1y lambda函数的类型签名?

时间:2014-02-09 09:53:09

标签: c++ c++11 lambda

我想知道是否有一种标准方法来获取任何给定lambda的参数的类型签名(即返回类型和类型)?

我问的原因是,我一直想知道auto这样的声明中的auto l =[](int x,int y)->int{return x+y;}类型究竟是什么。在auto的其他用例中,对于较长的类型名称而言,它是一种便利和更短的替代方案。但是对于lambdas,是否有另一种方法来声明lambda变量?

我的理解是标准lambda只不过是一个函数对象,它是它自己的类型。因此,即使两个lambda具有相同的返回类型和参数类型,它们仍然是两个不同的,不相关的类/仿函数。但是有一种方法可以捕捉到它们在类型签名方面相同的事实吗?

我认为我正在寻找的类型签名可能类似于正确类型的std::function<>对象。

更有用/涉及的问题是,如果可以提取类型签名,则可以编写一般包装函数将任何lambda函数转换为相同类型的std::function对象签名。

4 个答案:

答案 0 :(得分:15)

根据Can the 'type' of a lambda expression be expressed?,在当前的c ++中实际上有一种简单的方法(不需要c ++ 1y)来计算lambda的return_type和参数类型。为了适应这一点,为每个lambda组装一个std::function类型的签名类型(下面称为f_type)并不困难。

予。使用这种抽象类型,实际上可以使用auto的替代方式来表达lambda的类型签名,即下面的function_traits<..>::f_type。注意:f_type不是lambda的真实类型,而是函数术语中lambda类型签名的摘要。然而,它可能比lambda的真实类型更有用,因为每个lambda都是它自己的类型

如下面的代码所示,就像一个人可以使用vector<int>::iterator_type i = v.begin()一样,人们也可以function_traits<lambda>::f_type f = lambda,这是神秘的auto的替代品。当然,这种相似性只是正式的。下面的代码涉及将lambda转换为std::function,其中std::function对象构造的类型擦除成本以及通过std::function对象进行间接调用的成本较低。但是这些使用std::function的实现问题(我不相信它是基本的并且应该永远存在),毕竟,有可能明确地表达任何给定lambda的(抽象)类型签名。

II。也可以编写一个make_function包装器(非常类似std::make_pairstd::make_tuple)来自动转换lambda f(以及其他类似指针/仿函数的callables) 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;
}

答案 1 :(得分:11)

你是正确的C ++ 11 lambdas的类型是匿名的和实例唯一的。 std::function类型可以存储对我遇到过的任何一种lambda的引用,但据说它会受到性能影响。

尝试

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。

答案 2 :(得分:3)

在C ++中,有一般的lambdas,没有一个调用签名(operator()()是模板)。

答案 3 :(得分:1)

  

我想知道是否有一种标准方法来获取任何给定lambda的参数的类型签名(即返回类型和类型)?

不,没有。

  

我问的原因是我一直想知道auto声明中的auto l =[](int x,int y)->int{return x+y;}类型究竟是什么。

这是一个未指定的类类型,由实现创建。 lambdas的全部意义在于它们是“匿名函数”,即你不知道它们的类型。

如果你想要一个已知的类型,那么写一个函数对象类型。

  

在auto的其他使用案例中,对于较长的类型名称而言,它是一种便利和更短的替代方案。但是对于lambdas,是否有另一种方法来声明lambda变量?

没有

如果您想自己声明类型,请不要使用lambda表达式。

  

我的理解是标准lambda只不过是一个函数对象,它是它自己的类型。因此,即使两个lambda具有相同的返回类型和参数类型,它们仍然是两个不同的,不相关的类/仿函数。

正确。每个lamda表达式都会生成一个唯一的类型。

  

但是有一种方法可以捕捉它们在类型签名方面相同的事实吗?

不,没有语言功能允许这样做。

  

我认为我正在寻找的类型签名可能类似于std :: function&lt;&gt;正确类型的对象。

即使在C ++ 11中有可能,但在C ++ 14中它也无济于事,其中lambda表达式可以接受任何数字和任何类型的参数,例如: [](auto... a) { }

无论如何,如果你不知道你的lambda函数的调用签名,那么我会说你使用lambdas错了。当你编写lambda时,你应该知道它的属性是什么,所以当你知道它的属性时,要么立即使用它,要么尽可能早地将它放在std::function(或其它一些捕获其调用签名的类型)中。 。如果您正在创建lambdas并在非本地使用它们而您不知道呼叫签名,那么您做错了。