【问题标题】:Can we get the type of a lambda argument?我们能得到一个 lambda 参数的类型吗?
【发布时间】:2017-11-19 14:40:12
【问题描述】:

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

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

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++ visual-studio-2010 lambda c++11


    【解决方案1】:

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

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

    但是,我们正在从 C++0x 开始,这是有充分理由的。其中最简单的就是简单地重载:带有模板化operator() 的函子类型(又名多态函子)只需接受所有类型的参数;那么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;
    };
    

    [ 一个复杂的 trait 可以连续查询 lvalue-rvalue/const/volatile 重载,如果所有重载都相同,则公开第一个参数,或者使用std::common_type。]

    【讨论】:

    • 很好的解释谢谢。我选择了重新设计(类型“T”也是模板参数),但我只是希望能够以某种方式摆脱它,因为它似乎是不必要的重复。
    • 我的真实代码实际上确实已经捕获了变量(通常是调用setter方法的对象),但感谢您的澄清。
    • 难道你不能用&amp;F::operator() 和指向成员函数的指针而不是直接分析F 来做类似的事情吗?我认为这也适用于接受捕获的 lambda。
    • @Dennis :这适用于 lambda,但不适用于手写的多态函子或具有重载 operator() 的函子,这在我看来是不可行的。
    • 请注意,对于 C++17,您可以使用 std::function's deduction guides。在内部,它们要么使用函数指针,要么检查类型的operator()。这可以很容易地与手动类型特征结合以提取参数和返回类型。
    【解决方案2】:

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

    【讨论】:

    • 最初我报告了这个问题,但现在我意识到我犯了一个错误。 Here it is online.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-12
    相关资源
    最近更新 更多