2011-06-28 83 views
14

使用std::function,我们可以使用argument_type,second_argument_type等typedefs得到一个参数的类型,但是我看不到用lambdas做同样事情的方法。可能吗? (我使用VS2010)我们可以得到一个lambda参数的类型吗?

说我想是在用于读取对象,并把它传递给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); 
} 

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

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

但它不会与lambda表达式直接合作:

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

有没有办法以lambda和std::function对象的方式访问参数类型?也许一种方法可以首先获得lambda的std::function类型,然后从argument_type那里获得?


从下面的答案,与lambda表达式的作品和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); }); 

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

+2

简答:不。 – ildjarn

+0

长答案:是的,见下文。 –

回答

16

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

这将有可能要求每一个函数对象类型指定其参数类型,以及依次强制从lambda表达式生成的闭包类型是这样做的。事实上,像可适配函子这样的C++ 0x之前的特性只适用于这种类型。

但是,我们正在从C++ 0x开始,并有很好的理由。其中最简单的是简单的重载:一个具有模板化operator()(又名多态函子)的函数类型只需要所有类型的参数;那么argument_type应该是什么?另一个原因是通用代码(通常)试图指定对其操作的类型和对象的最少限制,以便更容易地(重新)使用。

换言之,通用代码是不是真的感兴趣给定Functor ftypename Functor::argumentint。这是更多有趣的是知道f(0)是一个可以接受的表达。对于这个C++ 0x提供的工具,如decltypestd::declval(方便地将两个包装在std::result_of内部)。

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


对于单形函子类型(即无重载),有可能来检查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) 

接受一个成员函数指针服用至少有一个参数。现在:

template<typename F> 
struct first_argument { 
    typedef decltype(helper(&F::operator())) type; 
}; 

[一个精心制作的特性有可能先后查询左值右值/常量/挥发性超载和暴露的第一个参数,如果是同样为所有重载,或使用std::common_type]

+0

很好的解释谢谢。我选择重新设计(类型'T'也是一个模板参数),但我只是希望我能够以某种方式摆脱它,因为它似乎是不必要的重复。 –

+0

我的真实代码实际上确实捕获了变量(通常是调用setter方法的对象),但感谢您的澄清。 –

+1

你不应该可以做类似的事情,但用'&F :: operator()'和指向成员函数的指针而不是直接分析'F'?这也适用于接受捕获的lambda表达式,我想。 –

2

@吕克的答案是伟大的,但我的对面,我也需要处理函数指针的情况下传来:

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; 

} 
+0

最初我报告了这个问题,但现在我意识到我犯了一个错误。 [它在线](http://coliru.stacked-crooked.com/a/7660f42b6e2a7476)。 – doug65536

相关问题