【问题标题】:Mapping a list to a different type将列表映射到不同的类型
【发布时间】:2020-12-30 21:38:04
【问题描述】:

我正在尝试创建一个List 类,主要是因为我想自己实现一些向量不存在的方法。其中之一是 map() 函数,您可以在其中传入一个 lambda 作为参数,该方法返回一个新的 List,其中旧列表中的每个值都已传递给函数。

我遇到的问题是获取处理返回具有不同类型的List 的方法。例如:

List<Option<int>> y;
y.append(option(1));
y.append(option(2));
y.append(option(3)); 
//y is [Option(1), Option(2), Option(3)]

List<int> z = y.map([](Option<int> o) {return o.get();});

//z should be [1,2,3]

map() 函数在其当前状态下定义为:

template<typename S>
List<S> map(S (* function)(T)) {
    List<S> output;     
    for (int i = 0; i < size(); i++) {
        output.append(function((*this)[i]));
    }
    return output;
}

问题是上面似乎不允许我传入一个lambda,只能传入一个先前定义的函数,这违背了该方法方便的目的。我可以让它接受 lambda 的唯一方法是将函数定义为:

template<typename S, typename R>
List<S> map(R function) {
    List<S> output;  
    for (int i = 0; i < size(); i++) {
        output.append(function((*this)[i]));
    }
    return output;
}

但这导致了一个不同的问题,它说:

没有函数模板 'List::map[with T=Option]' 的实例与参数列表匹配

是否有特定的解决方法,或者我每次调用map() 函数时都必须声明函数?

【问题讨论】:

  • C++ 惯用地图称为std::transform,顺便说一句。
  • 您应该查看auto 返回类型和type_traits 库,尤其是std::invoke_result
  • S 仅用于方法。我的目的是澄清 List 类型可能与调用该方法的列表的类型不同。我仍然习惯于 C++(我更习惯于 Scala),所以这就是我的心态。此外,查看 std::transform (en.cppreference.com/w/cpp/algorithm/transform) 似乎它直接改变了被调用的值。我的目标是坚持功能范式,因此该方法旨在一起返回一个新列表,而不是更改列表本身。
  • @GLGuy std::transform(input.begin(), input.end(), std::back_inserter(output), function) 是一个东西。还有transform_view,C++20 中的新功能,它与旧的 C++ 做事方式有很大不同,但你应该更熟悉。

标签: c++ list dictionary templates lambda


【解决方案1】:

使用decltype。在您的定义中,您希望使用函数返回的内容来确定 output 列表返回值的类型。所以你可以这样做:

template<typename R>
auto map(R function) {
    List<std::decay_t<decltype(function(operator[](0)))>> output;
    for(int i = 0; i < size(); i++) output.append(function(operator[](i)));
    return output;
}

列表元素的类型是functionthis 的元素上调用时返回的任何内容。 std::decay_t 将您无法存储的类型(引用、cv 限定类型、数组等)转换为您可以存储的类型(分别是非引用、非限定类型、指针等)。

使用std::invoke_result 可以获得更强大的结果,它可以让您将函数调用替换为std::invoke

template<typename T>
struct List {          // Your list class, we need the T
    template<typename R>
    auto map(R function) {
        List<std::decay_t<std::invoke_result_t<R, T&>>> output; // assuming that accesses give references
        for(int i = 0; i < size(); i++) output.append(std::invoke(function, operator[](i)));
        return output;
    }
};

结果是,现在您还可以map 诸如指向您列表中成员的指针之类的东西。

Godbolt

【讨论】:

  • @HTNW,谢谢你的编辑,但是这个级别的答案是重写的,你真的应该自己做答案。
  • 我并没有真正添加任何你没有说的材料(除了std::invoke 的东西),所以我觉得这样做不舒服。希望没关系。
  • @HTNW,对我来说不是问题,这是一个很好的编辑。只是您为我付出了这么多努力,这对您来说可能是一个更好的答案。
  • 使用 decltype 让它工作。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-27
  • 1970-01-01
  • 2022-01-23
  • 2020-05-02
相关资源
最近更新 更多