【问题标题】:How to combine projections with transform in C++20 ranges如何在 C++20 范围内将投影与变换结合起来
【发布时间】:2021-09-09 12:34:03
【问题描述】:

C++20 ranges::sort 支持投影,这很好,但我想做更复杂的事情,特别是对投影成员上的函数结果进行排序。

对于函数调用,我尝试了转换然后对该视图进行排序,但 C++20 排序似乎不适用于视图,answer 解释了原因(对于 range-v3)。

换句话说,我可以在不使用 lambda/functor 的情况下编写 this 吗?

struct S{
    int val;
};

int origin = 47;

// assume we can not change Distance to take S
int Distance(int val){
    return std::abs(val - origin);
}

void my_sort(std::vector<S> ss) {
  std::ranges::sort(ss, std::greater<>{}, [](const S& s){
      return Distance(s.val);
  });
}

【问题讨论】:

  • lambda 有什么问题?你可以创建一个函数来拥有类似Chained(&amp;S::val, &amp;Distance)...
  • 另一种方法是在struct中实现bool operator &lt; (const S&amp; ss) const并使用std::sort
  • @Jarod42 没什么特别的,如果不需要的话,我只是不想写 lambda,例如,我真的很喜欢投影,因为它们让我可以做很多以前需要 lambda 的事情。跨度>
  • 类似compose from boost::hof?
  • @Caleth 是的,这在接受的答案中,很好的建议

标签: c++ c++20 std-ranges


【解决方案1】:

好像你想要函数组合:

void my_sort(std::vector<S>& ss) {
  std::ranges::sort(ss, std::greater<>{}, compose(Distance, &S::val));
}

compose(f, g)(x) 在哪里 f(g(x))。 Boost.Hof 有一个compose,在 range-v3 中也有一个。但标准库中没有。


对于函数调用,我尝试了转换,然后对该视图进行排序,

sort(r, less(), f)sort(r | transform(f), less()) 之间有很大的区别:前者使用函数f 作为排序键对r 进行排序,后者对转换后的值进行专门排序。一个展示差异的具体例子:

struct X {
    int i;
    int j;
};

vector<X> xs = {X{1, 2}, X{2, 1}, X{0, 7}};

// this would give you (0, 7), (1, 2), (2, 1)
// because we're sorting by the i
ranges::sort(xs, less(), &X::i);

// this would have given you (0, 2), (1, 1), (2, 7)
// because we're sorting the i's independently
ranges::sort(xs | views::transform(&X::i), less());

【讨论】:

  • 不直观,但这是有道理的,我认为“转换”项目仍然被视为一个对象,因为在视图迭代器中有 .base ......但我想当我考虑它时这也是有道理的. :) 我认为没有办法礼貌地要求 std::ranges::sort “对整个对象进行排序”(使用 .base())
  • @NoSenseEtAl 我不同意这是不直观的。 sort 对你给它的东西进行排序。如果你给它的是i 成员,它会对i 成员进行排序。如果要对整个X 进行排序,则要求它对Xs 进行排序。这也是键中的最大值 -- ranges::max(f, less(), key) -- 和最大键 -- ranges::max(f | transform(key)) 之间的差异。
  • @NoSenseEtAl 此外,如果您的转换返回纯右值,则它们没有要交换的地址。返回纯右值的投影很好,因为比较会立即消耗它
  • @Barry 现在我想你是对的,我想我的期望是基于旧的 std::sort ,它从未在 transform_view 上工作过。
  • ...或者,如果您使用的实现没有实现 LWG3520,那么views::transform 版本可以任意置换剩余部分。
【解决方案2】:

如果您的S 只有一个成员,您可以使用views::transform

void my_sort(std::vector<S> ss) {
  std::ranges::sort(
    ss | std::views::transform(&S::val), std::ranges::greater{}, Distance
  );
}

Demo.

【讨论】:

  • 很奇怪,我可以发誓我尝试过 views::transform 和排序失败...,让我尝试重新创建它
  • 我正在使用距离进行转换,但没有成功。 godbolt.org/z/4Ejcxhnnz不知道为什么你的例子有效,我的没有。
  • 这交换了Sints。如果它有其他数据成员,他们就会被抛在后面
  • @Caleth。所以我说“如果你的 S 只有一个成员”。
  • 如果你的 S 只有一个成员并且它是可复制的?
猜你喜欢
  • 1970-01-01
  • 2014-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-22
  • 2018-06-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多