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

时间:2011-06-28 19:25:06

标签: c++ visual-studio-2010 lambda c++11

使用std::function,我们可以使用argument_typesecond_argument_type等typedef来获取参数的类型,但我看不到用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);
}

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

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

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

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

有没有办法以一种适用于lambdas和std::function对象的方式访问参数类型?也许首先获得lambda的std::function类型,然后再获取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); });

由于int在这里重复,我希望摆脱它 - 对于int来说并不是那么糟糕,但对于几个命名空间中的长命名类型更令人讨厌。 C'est la vie!

2 个答案:

答案 0 :(得分:20)

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

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

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

换句话说,对于Functor ftypename 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)

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

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

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

答案 1 :(得分:5)

@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;

}