C++ 我们能得到lambda参数的类型吗?

C++ 我们能得到lambda参数的类型吗?,c++,visual-studio-2010,lambda,c++11,C++,Visual Studio 2010,Lambda,C++11,使用std::function,我们可以使用argument\u type、second\u argument\u type等来获取参数的类型。typedefs,但我看不到一种方法可以对lambdas执行相同的操作。可能吗?(我正在使用VS2010) 假设我希望在我的反序列化系统中使用以下类似的内容来读取对象并将其传递给setter函数: template<typename F> static void forward(F f) { // Create an object

使用
std::function
,我们可以使用
argument\u type
second\u argument\u type
等来获取参数的类型。typedefs,但我看不到一种方法可以对lambdas执行相同的操作。可能吗?(我正在使用VS2010)

假设我希望在我的反序列化系统中使用以下类似的内容来读取对象并将其传递给setter函数:

template<typename F> 
static void forward(F f)
{ 
    // Create an object of the type of the first
    // parameter to the function object F
    typedef typename F::argument_type T;
    T t;

    //...do something with 't' here (deserialize in my case)

    // Forward the object to the function
    f(t);
}

由于
int
在这里重复出现,我希望将其删除-对于
int
来说不是那么糟糕,但是对于几个名称空间中的长名称类型来说更烦人。这就是生活

在一般情况下,这是不可取的。(请注意,
std::function
很容易指定什么,例如
参数_type
是什么:它只是
A
!在类型定义中可用。)

可以要求每个函数对象类型指定其参数类型,并反过来要求由lambda表达式生成的闭包类型这样做。事实上,C++0x之前的特性(如适应性函子)只适用于此类类型

然而,我们在使用C++0x时正从这一点出发,这是有充分理由的。其中最简单的就是重载:带有模板化的
操作符()
(又称多态函子)的函子类型只接受所有类型的参数;那么
参数类型应该是什么呢?另一个原因是泛型代码(通常)试图对其操作的类型和对象指定最少的约束,以便更容易(重新)使用

换句话说,对于给定的
函子f
类型名函子::参数
be
int
,泛型代码并不真正感兴趣。更有趣的是知道
f(0)
是一个可接受的表达式。为此,C++0x提供了一些工具,如
decltype
std::declval
(方便地将这两个工具打包在
std::result\u中)

在我看来,您有两个选择:要求传递到模板的所有函子都使用C++03风格的约定,即指定
参数类型
等等;使用下面的技巧;或者重新设计。我推荐最后一个选项,但这是你的决定,因为我不知道你的代码库是什么样子,或者你的需求是什么


对于单态函子类型(即无重载),可以检查
运算符()
成员。这适用于lambda表达式的闭包类型

所以我们宣布这些助手

template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...));

template<typename F, typename Ret, typename A, typename... Rest>
A
helper(Ret (F::*)(A, Rest...) const);

// volatile or lvalue/rvalue *this not required for lambdas (phew)
模板
A.
助手(Ret(F::*)(A,Rest…);
模板
A.
助手(Ret(F::*)(A,Rest…)const);
//挥发性或左值/右值*lambdas不需要此选项(phew)
接受指向至少包含一个参数的成员函数的指针。现在:

template<typename F>
struct first_argument {
    typedef decltype( helper(&F::operator()) ) type;
};
模板
结构第一参数{
typedef decltype(helper(&F::operator()))类型;
};

[一个精心设计的特性可以连续查询左值rvalue/const/volatile重载并公开第一个参数,如果它对所有重载都相同,或者使用
std::common_type

@Luc的答案很好,但我刚刚遇到了一个我还需要处理函数指针的情况:

template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));
void function(float);

struct functor {
    void operator() (int);
};

int main() {
    std::cout << std::is_same<first_argument<functor>, int>::value
              << ", "
              << std::is_same<first_argument<decltype(&function)>, int>::value 
              << std::endl;
    return 0;

}
模板
Arg第一个参数辅助对象(Ret(*)(Arg,Rest…);
模板
Arg第一个参数助手(Ret(F::*)(Arg,Rest…);
模板
Arg第一个参数(Ret(F::*)(Arg,Rest…)常量);
模板
decltype(第一个参数辅助对象(&F::operator()))第一个参数辅助对象(F);
模板
使用first_argument=decltype(first_argument_helper(std::declval());
这可用于函子和函数指针:

template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));

template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);

template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);

template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));
void function(float);

struct functor {
    void operator() (int);
};

int main() {
    std::cout << std::is_same<first_argument<functor>, int>::value
              << ", "
              << std::is_same<first_argument<decltype(&function)>, int>::value 
              << std::endl;
    return 0;

}
void函数(float);
结构函子{
void操作符()(int);
};
int main(){

std::非常好的解释,谢谢。我选择了重新设计(类型“T”也是一个模板参数),但我只是希望我能够以某种方式摆脱它,因为它看起来像是不必要的重复。我真正的代码实际上捕获了变量(通常是调用setter方法的对象)但是谢谢你的澄清。难道你不能用
&F::operator()做类似的事情吗
和指向成员函数的指针,而不是直接分析
F
?我认为这对接受捕获的lambda也适用。@Dennis:这对lambda适用,但不适用于手工编写的多态函子或重载
运算符()的函子
,这在我看来是不可行的。请注意,对于C++17,您可以使用。在内部,它们使用函数指针或检查类型的
运算符()
。这可以很容易地与手动类型特征相结合,以提取参数和返回类型。最初我报告了这方面的问题,但现在我意识到我犯了一个错误。
void function(float);

struct functor {
    void operator() (int);
};

int main() {
    std::cout << std::is_same<first_argument<functor>, int>::value
              << ", "
              << std::is_same<first_argument<decltype(&function)>, int>::value 
              << std::endl;
    return 0;

}