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

2021-12-14 00:00:00 lambda c++ c++11 visual-studio-2010

使用 std::function,我们可以使用 argument_typesecond_argument_type 等 typedef 来获取参数的类型,但我可以看不到用 lambda 做同样事情的方法.是否可以?(我使用的是 VS2010)

Using std::function, we can get the type of an argument using the argument_type, second_argument_type etc. typedefs, but I can't see a way to do the same thing with lambdas. Is it possible? (I'm using VS2010)

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

Say I want something like the following in my deserialization system used to read an object and pass it to a setter function:

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);
}

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

It can be used like this and everything works fine:

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

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

But it will not work directly with lambdas:

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 的方法?

Is there a way to access the parameter types in a way that will work for both lambdas and std::function objects? Maybe a way to get the std::function type of a lambda first, and then the argument_type from that?

根据下面的答案,适用于 lambdas 和 std::function 的版本是:

Following on from the answer below, a version that works with lambdas and std::function is:

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!

Since int is repeated here I was hoping to get rid of it - not so bad for int but more annoying for long-named types in a couple of namespaces. C'est la vie!

推荐答案

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

It's not desirable in the general case. (Note that it's quite easy for std::function<T(A)> to specify what e.g. argument_type is: it's just A! It's available in the type definition.)

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

It would be possible to require each and every function object type to specify its argument types, and in turn mandate that the closure types generated from lambda expression do so. In fact, pre-C++0x features like adaptable functors would only work for such types.

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

However, we're moving from that with C++0x and for good reasons. The simplest of which is simply overloading: a functor type with a templated operator() (a.k.a a polymorphic functor) simply takes all kind of arguments; so what should argument_type be? Another reason is that generic code (usually) attempts to specify the least constraints on the types and objects it operates on in order to more easily be (re)used.

换句话说,对于给定的 Functor f, typename Functor::argumentint.知道 f(0) 是一个可接受的表达式,更有趣.为此,C++0x 提供了诸如 decltypestd::declval 之类的工具(方便地将两者打包在 std::result_of 中).

In other words, generic code is not really interested that given Functor f, typename Functor::argument be int. It's much more interesting to know that f(0) is an acceptable expression. For this C++0x gives tools such as decltype and std::declval (conveniently packaging the two inside std::result_of).

在我看来,您有两种选择:要求传递给模板的所有函子使用 C++03 风格的约定,指定 argument_type 等;使用以下技术;或重新设计.我建议使用最后一个选项,但这是您的决定,因为我不知道您的代码库是什么样的或您的要求是什么.

The way I see it you have two choices: require that all functors passed to your template use a C++03-style convention of specifying an argument_type and the like; use the technique below; or redesign. I'd recommend the last option but it's your call since I don't know what your codebase looks like or what your requirements are.

对于单态函子类型(即没有重载),可以检查 operator() 成员.这适用于 lambda 表达式的闭包类型.

For a monomorphic functor type (i.e. no overloading), it is possible to inspect the operator() member. This works for the closure types of lambda expressions.

所以我们声明这些助手

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)

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

that accept a pointer to member function taking at least one argument. And now:

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

[一个精心设计的特征可以连续查询左值-右值/常量/易失性重载,如果第一个参数对所有重载都相同,则公开第一个参数,或者使用 std::common_type.]

[ an elaborate trait could successively query the lvalue-rvalue/const/volatile overloads and expose the first argument if it's the same for all overloads, or use std::common_type.]

相关文章