返回模板 lambda 函数的正确方法是啥?

Posted

技术标签:

【中文标题】返回模板 lambda 函数的正确方法是啥?【英文标题】:What is the correct way to return a template lambda function?返回模板 lambda 函数的正确方法是什么? 【发布时间】:2021-10-30 14:42:18 【问题描述】:

我正在用 C++ 中的 lambda 函数进行试验,我想制作一个大致相当于 Haskell 中的 take 函数的函数。

take :: Int -> [a] -> [a]

给定一个整数 n 和一个包含任何类型 a 的列表,它返回列表的第一个 n 元素。我试图用 C++ 中的柯里化函数来做到这一点,但遇到了使其成为多态的问题。我目前的非多态实现是这样的:

function<vector<int>(vector<int>)> take(int n) 
  return [=](vector<int> v) 
    v.resize(n);
    return v;
  ;

这显然只适用于vector&lt;int&gt; 类型,我想知道如何使它在向量元素的类型中具有多态性。到目前为止,我已经尝试了此代码的一些变体:

template<typename T>
function<vector<T>(vector<T>)> take(int n) 
  return [=](vector<T> v) 
    v.resize(n);
    return v;
  ;

但是当我尝试使用类似的东西部分应用该功能时

function<vector<int>(vector<int>)> take_2 = take(2);

我收到以下错误:

note: candidate template ignored: couldn't infer template argument 'T'
function<vector<T>(vector<T>)> take(int n) 
                               ^

非常感谢您对实现这一目标的任何帮助!

我对 C++ 中的模板和 lambdas 也非常缺乏经验,所以如果有更优雅的方法来解决这个问题,请告诉我。

【问题讨论】:

C++ 没有多态函数(没有模板)或健壮的类型推断。如果你需要 Haskell,你知道在哪里可以找到它。 Template instantiation can only deduce its parameters from the arguments 【参考方案1】:

您应该返回一个带有模板化调用运算符的对象,该调用运算符可以使用resize 方法和复制/移动构造函数处理任何事情。一个 lambda 函数可以做到这一点:

#include <deque>
#include <cstdio>

auto take(size_t n) 
    return [n](auto container) 
        container.resize(n);
        return container;
    ;


int main() 
    auto take_2 = take(2);
    std::deque<int> d;
    auto d2 = take_2(d);
    std::printf("size of d2 is %zu\n", d2.size());

【讨论】:

【参考方案2】:

take 是一个带有不可演绎模板参数的函数模板。当对应的函数形参依赖于模板形参时,可以从函数实参推导出模板形参;但int 不依赖于T

与其他一些语言中类型推断的工作方式不同,C++ 不允许从调用结果的使用方式中推断模板参数。

调用take的唯一方法是显式指定模板参数,例如take&lt;std::string&gt;(42)

A Rank 2 Haskell 函数

take' :: int -> (forall a . [a] -> [a])

可以用一个 C++ 函数来近似,该函数返回一个带有operator() 成员模板的函数对象(例如 lambda):

#include <iostream>
#include <vector>
#include <string>

auto take(std::size_t n) 
   return [n](auto xs) 
     if (xs.size() > n)
       xs.resize(n);
     return x;
   ;


auto show(const auto& xs) 
    for (const auto& x: xs) std::cout << x << " ";
    std::cout << "\n";
;

int main()

   const auto take2 = take(2);
   show(take2(std::vector1,2,3,4,5));
   show(take2(std::vector"a","b","c","d","e"));

【讨论】:

以上是关于返回模板 lambda 函数的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

返回执行的转场的正确方法是啥?

lambda表达式由啥组成

更改 UITextView 的键盘返回按钮的正确方法是啥

异步 Lambda 函数:返回 promise 或发送 responseURL 不会终止 CloudFormation 自定义资源调用

验证对象是不是存在于 django 视图中而不返回 404 的正确方法是啥?

验证对象是不是存在于 django 视图中而不返回 404 的正确方法是啥?