我们可以得到lambda参数的类型吗?

Chr*_*eld 22 c++ lambda visual-studio-2010 c++11

使用std::function,我们可以使用得到一个参数的类型argument_type,second_argument_type等等类型定义,但我看不到的方式做同样的事情lambda表达式.可能吗?(我正在使用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);
}
Run Code Online (Sandbox Code Playgroud)

它可以像这样使用,一切正常:

std::function<void(int)> f = [](int i) -> void { setValue(i); };
forward(f);
Run Code Online (Sandbox Code Playgroud)

但它不能直接与lambdas一起使用:

forward([](int i) -> void { setValue(i); });
//error C2039: 'argument_type' : is not a 
//member of '`anonymous-namespace'::<lambda1>'
Run Code Online (Sandbox Code Playgroud)

有没有办法以一种适用于lambdas和std::function对象的方式访问参数类型?也许是一种先获得std::functionlambda类型的方法,然后argument_type从中获取?


继下面的答案后,一个与lambdas一起使用的版本std::function是:

template<typename T, typename F> 
static void forward(F f)
{ 
    T t;

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

    f(t);
}

forward<int>([](int i) -> void { setValue(i); });
Run Code Online (Sandbox Code Playgroud)

由于int在这里重复,我希望摆脱它 - 不是那么糟糕,int但对于几个命名空间中的长命名类型更烦人.这就是生活!

Luc*_*ton 22

在一般情况下,这是不可取的.(请注意,很容易std::function<T(A)>指定例如argument_type:它只是A!它在类型定义中可用.)

可以要求每个函数对象类型指定其参数类型,并依次强制从lambda表达式生成的闭包类型这样做.实际上,像适应性仿函数这样的前C++ 0x特性只适用于这种类型.

但是,我们正在使用C++ 0x,并且有充分的理由.最简单的就是简单重载:带有模板operator()(也就是多态函子)的仿函数类型只需要所有类型的参数; 应该argument_type是什么?另一个原因是通用代码(通常)试图指定对其操作的类型和对象的最小约束,以便更容易(重新)使用.

换句话说,通用代码是不是真的有兴趣,鉴于Functor f,typename Functor::argumentint.这是有趣的知道,f(0)是一个可以接受的表达.对于这个C++ 0x提供了诸如decltypestd::declval(方便地将两者内部打包std::result_of)的工具.

我看到它的方式有两个选择:要求传递给模板的所有仿函数都使用C++ 03式约定argument_type等等; 使用下面的技术; 或重新设计.我推荐最后一个选项,但这是你的电话,因为我不知道你的代码库是什么样的,或者你的要求是什么.


对于单形仿函数类型(即没有过载),可以检查该operator()成员.这适用于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)
Run Code Online (Sandbox Code Playgroud)

接受指向至少一个参数的成员函数的指针.现在:

template<typename F>
struct first_argument {
    typedef decltype( helper(&F::operator()) ) type;
};
Run Code Online (Sandbox Code Playgroud)

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


Til*_*aux 7

@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>()));
Run Code Online (Sandbox Code Playgroud)

这可以在函子和函数指针上使用:

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;

}
Run Code Online (Sandbox Code Playgroud)