【问题标题】:Does c++20 ranges have any view functions that do not return value?c++20 范围是否有任何不返回值的视图函数?
【发布时间】:2022-11-02 22:29:46
【问题描述】:

我可以使用 std::views::transform 创建新的 stream-style 容器,然后打印它,如下所示:

#include<iostream>
#include<vector>
#include<ranges>
using namespace std;
int main() {
    // clang -std=c++20
    std::vector<int> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    auto output = input 
        | std::views::filter([](const int n) {return n % 3 == 0; })
        | std::views::transform([](const int n) {return n * n; });
    for (auto o : output) {
        cout << o << endl;
    }
    return 0;
}

是的,它有效,但我希望简单地将我的for 循环写入| 连接的管道中,有没有办法将代码更改为:

input 
        | std::views::filter([](const int n) {return n % 3 == 0; })
        | std::views::transform([](const int n) {return n * n; })
        | std::views::SOME_FUNCTION(cout<<n<<endl);

这避免了我的for 循环。

所以我的问题是:std::views 是否有 SOME_FUNCTION 可以满足我的需求?

【问题讨论】:

  • 你的for-loop 很简单。

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


【解决方案1】:

您正在寻找的不是视图。其实我不完全确定你在找什么,但也许是std::ranges::for_each

#include<iostream>
#include<vector>
#include<ranges>
#include <algorithm>
using namespace std;
int main() {
    // clang -std=c++20
    std::vector<int> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    
    std::ranges::for_each(input 
        | std::views::filter([](const int n) {return n % 3 == 0; })
        | std::views::transform([](const int n) {return n * n; }),
        [](auto x){std::cout << x << endl;});
}

【讨论】:

  • 我觉得 OP 正在 Java 的 Stream::forEach 或 C# 的 .ForEach 中寻找一些东西。
  • 谢谢,但我试过clang14和g++12,他们都说error: no member named 'for_each' in namespace 'std::ranges',我指定了-std=c++20或-std=c++2a或-std=c++2b,没有一个得到编译。而且vc2022也不行
  • @Troskyvs 你是#inculde &lt;algorithm&gt; 吗?
  • 只需将上面的代码复制到godbolt.org/z/cznTTdjes 就可以正常工作,即使使用 gcc 11.2
【解决方案2】:

C++20(或 C++23)中没有这样的适配器。我还发现缺少这个功能,因为它可以让程序员转向更一致和更“纯的" 函数式风格,而不是通过管道 (|) 组成表达式,然后在循环中使用它。

最终,我希望 std::views::for_each(或者 std::actions::for_each,如果 actions 达到标准)可以做到这一点。作为马上, 你可以实现你自己的for_each 这是可管道的,但不幸的是,that woulnd't be as standard as one would like这将成为 C++23 的非标准,因为使用 C++23 定义rande-adaptors 的正确方法是使用range_adaptor_closure&lt;T&gt;。此外,根据P2387R3

涉及类型对象的表达式的行为简历如果重载决议选择程序定义的operator| 函数,则D 作为| 运算符的操作数是未定义的。

direct source

因此,在 C++23 中,您可以轻松地创建自己的 terminal::for_each,以完全符合标准的方式使用函数式样式中的范围。

不过,就目前而言,最安全的做法是坚持基于范围的for 循环或std::ranges::for_each

【讨论】:

  • Boost中有实现吗?
  • @Eljay 在Boost 中没有实现这样的for_eachBoost 为您提供a consistent way of additing your own pipeable adaptors
  • 您可以相当轻松地编写可管道化的for_each
  • @Caleth 请参考我在the first link 中提供的答案。虽然这确实有效,但它不是“适配器的标准扩展”。没有统一的方法来做到这一点。有希望有一个 C++23 提案可以标准化这个过程,但它似乎没有成功。
  • @Fureeish this 怎么样?你想能够auto pipe = adaptor1 | adaptor2; auto res = range | pipe;吗?我错过了什么吗?
【解决方案3】:

std::views 被设计成懒惰的:除非您循环访问它们,否则它们不会做任何事情。打印范围内所有元素的功能与该设计并不真正兼容。

但是自己实现这样的设施并不是很困难。所有你需要的是:

template<class F>
struct for_each {
    F fn;

    friend void operator|(auto&& r, for_each my) {
        for (auto o : r) {
            my.fn(o);
        }
    }
};

然后你可以写:

int main() {
    std::vector<int> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    input 
        | std::views::filter([](const int n) {return n % 3 == 0; })
        | std::views::transform([](const int n) {return n * n; })
        | for_each([](auto o) { cout << o << endl; });
    return 0;
}

(如果您想支持标准范围适配器支持的所有不寻常的使用,这将更加困难,但是您想要吗?)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-21
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    相关资源
    最近更新 更多