标签: std-ranges

生成器在 C++20 视图管道中调用两次

在一个简单的views适配器管道中,调用一个gen函数来生成一系列值(使用内部状态),然后对其进行过滤。

令人惊讶和违反直觉的(至少对我来说)是这样的事实:生成器函数在每次迭代中被调用两次,因此对同一过滤器的下一次检查失败(过滤后的值不会在管道中重用)。

您知道这是否是正确的预期行为(以及为什么)?

libstdc++在 GCC 10.3、11.1 和 trunk(代码)以及range-v3GCC 和 clang(代码)中进行了测试。

int main() {
  int n = 0;
  auto gen = [&n]() {
    auto result = ++n;
    std::cout << "Generate [" << result << "]\n";
    return result;
  };

  auto tmp =
      ranges::views::iota(0)
      | ranges::views::transform([gen](auto &&) { return gen(); })
      | ranges::views::filter([](auto &&i) {
          std::cout << "#1 " << i << " " << (i % 2) << "\n";
          return (i …
Run Code Online (Sandbox Code Playgroud)

c++ range-v3 c++20 std-ranges

7
推荐指数
1
解决办法
732
查看次数

C++中的split_view和lazy_split_view有什么区别?

我已经阅读了最新的草案,其中lazy_split_view添加了内容。

但后来我发现它split_view改名为lazy_split_view,而且又split_view更新了。

libstdc++最近还通过使用GCC Trunk版本https://godbolt.org/z/9qG5T9n5h实现了这一点

我这里有一个简单的天真的程序,它显示了两个视图的用法,但我看不到它们的区别:

#include <iostream>
#include <ranges>

int main(){

    std::string str { "one two three  four" };

    for (auto word : str | std::views::split(' ')) {
        for (char ch : word)
            std::cout << ch;
        std::cout << '.';
    }

    std::cout << '\n';

    for (auto word : str | std::views::lazy_split(' ')) {
        for (char ch : word)
            std::cout << ch;
        std::cout << '.';
    }

}
Run Code Online (Sandbox Code Playgroud)

输出:

one.two.three..four.
one.two.three..four. …
Run Code Online (Sandbox Code Playgroud)

c++ std-ranges c++23

7
推荐指数
1
解决办法
2123
查看次数

为什么我不能在 `const filter_view` 上调用 `ranges::begin` ?

我无法ranges::begin致电const filter_view

https://en.cppreference.com/w/cpp/ranges/filter_view 和似乎不是begin。这是为什么?endconst

int main(){
    std::vector v{1,2,3};
    // removing const will make it compile
    const auto r = v | ranges::views::filter ([](auto&&){return true;}); 
    ranges::begin(r);
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/4feaYc

c++ std-ranges

6
推荐指数
1
解决办法
565
查看次数

为什么 range::basic_istream_view::begin() 不被缓存?

我发现c++20与range-v3ranges::basic_istream_view版本略有不同。

最重要的区别是std::ranges::basic_istream_view不会缓存其begin(),因此每个begin()s 将返回具有已读取值的下一个迭代器(godbolt):

auto words = std::istringstream{"today is yesterday's tomorrow"};
auto view = std::ranges::istream_view<std::string>(words);
std::cout << *view.begin() << "\n"; // today
std::cout << *view.begin() << "\n"; // is
std::cout << *view.begin() << "\n"; // yesterday's
std::cout << *view.begin() << "\n"; // tomorrow
Run Code Online (Sandbox Code Playgroud)

考虑以下内容(godbolt),如果我使用 range-v3 版本,则所有三个std::ranges::find()s 都会找到"is",但如果我使用 std 版本,"is"则只会在第一次调用中找到。

auto words = std::istringstream{"today is yesterday's tomorrow"};
auto view = std::ranges::istream_view<std::string>(words);
std::cout << …
Run Code Online (Sandbox Code Playgroud)

c++ range-v3 c++20 std-ranges

6
推荐指数
2
解决办法
237
查看次数

如何使用 std::ranges 加入视图

如何使用 std::ranges 连接两个视图?
在ranges-v3中,视图与views::concat()连接,我不知道如何使用std::ranges来做到这一点。

#include <string>
#include <iostream>
#include <vector>
#include <ranges>
using namespace std;
using namespace views;

void snake_to_camel() {   
    auto transform = views::transform;
    auto const s = string{"feel_the_force"};
    auto words = s | split('_');                        // [[f,e,e,l],[t,h,e],[f,o,r,c,e]]
    auto words_cap = words | transform([](auto word){
        auto transform = views::transform;
        auto head = word | take(1) 
                         | transform([](unsigned char c){return toupper(c);}); // e.g. [F]
        auto tail = word | drop(1);                     // e.g. [e.e.l]
        return tail;
        //return concat( head, tail );                  // …
Run Code Online (Sandbox Code Playgroud)

c++ c++20 std-ranges

6
推荐指数
1
解决办法
1910
查看次数

在 C++ 中使用范围是否可取?

我发现大多数 C++ stl 算法的传统语法很烦人;使用它们编写起来很长只是一个小问题,但它们总是需要对现有对象进行操作,这在很大程度上限制了它们的可组合性。

我很高兴看到 stl 中范围的出现;然而,从 C++20 开始,存在严重的缺点:标准库的不同实现对此的支持各不相同,并且 range-v3 中存在的许多内容并未进入 C++20,例如(对我来说)非常令人惊讶),将视图转换为向量(对我来说,如果我无法将计算结果存储在向量中,这会使这一切变得毫无用处)。

另一方面,使用 range-v3 对我来说似乎也不理想:它的文档很少(而且我不同意其中的所有内容都是不言自明的),而且更严重的是,C++20 的想法range 与 range-v3 所做的不同,所以我不能只是说,好吧,让我们坚持使用 range-v3;无论如何,这将在某个时候成为标准。

那么,我应该使用两者中的任何一个吗?或者这一切都不值得,并且通过依赖 std 范围或 range-v3,使我的代码太难以维护和移植?

c++ range-v3 c++20 std-ranges

6
推荐指数
1
解决办法
3901
查看次数

使用 std::views::join 是否存在基本的性能成本?

我最近一直在对范围和视图进行一些性能评估。我发布了一个简单的示例(也在https://www.godbolt.org/z/7ThxjKafc),其中汇编的差异比我预期的要显着得多。使用最新的 GCC 和 -O3,

  • 的汇编sum_array包含 31 条指令和 8 个跳转。
  • 的汇编sum_vec包含 12 条指令和 2 个跳转。

鉴于 的大小m_array在编译时已知,我预计这两个函数的汇编几乎相同。我是否应该期望优化编译器在未来版本中得到改进,或者在如何std::views::join指定方面是否存在一些基本限制?

#include <array>
#include <vector>
#include <ranges>

struct Foo {
    auto join() const { return m_array | std::views::join; }
    auto direct() const { return std::views::all(m_array[0]); }
    std::array<std::vector<int*>, 1> m_array;
};
__attribute__((noinline)) int sum_array(const Foo& foo)
{
    int result = 0;
    for (int* val : foo.join())
        result += *val;
    return result;
}
__attribute__((noinline)) int sum_vec(const Foo& …
Run Code Online (Sandbox Code Playgroud)

c++ performance gcc c++20 std-ranges

6
推荐指数
0
解决办法
298
查看次数

使用 C++20 范围避免循环

我被分配了一项任务,需要在考虑到几个限制的情况下解决问题。重点是强制使用 STL 算法、迭代器和新的 c++20 功能,包括ranges. 然而,我已经阅读了几个小时的范围,但我仍然无法弄清楚如何在考虑到所有限制的情况下实现该问题。我简化了问题并删除了具体细节以使其更加通用。

问题:

编写一个函数,该函数 1) 接受自定义对象的向量2) 返回不同类型input的向量,其中包含满足某些条件的每个对象的元素。添加的值基于输入对象的属性。input

我意识到这可能听起来很晦涩,所以这里有一个简单的例子。对于形状的输入向量,其中每个形状都有一个名称和一个区域:

vector<Shapes> input{ { "Square", 10 }, { "Triangle", 30 } , { "Square", 1 }, { "Circle", 30 }, { "Triangle", 15 } };
Run Code Online (Sandbox Code Playgroud)

返回枚举向量

enum Color { RED, BLUE, GREEN };
Run Code Online (Sandbox Code Playgroud)

这样为每个 Square 或 Circle 添加一个枚举。枚举的值是根据每个 Shape 的面积确定的。因此,假设面积大于 20,则添加红色,否则添加绿色。

所以在这种情况下我们会返回{ GREEN, GREEN, RED }

这一切都很好,可以通过多种方式实施,但限制因素使其变得非常困难。

限制条件:

  • 没有循环或递归
  • std::for_each
  • 没有其他数据结构std::vector
  • 没有内存分配(除了返回向量的一次性分配)
  • 没有通过引用 lambda 捕获或可变 lambda
  • 无法修改输入向量

我的教授声称“c++20 范围使这项任务特别简单”。但即使在阅读了几个小时的范围后,我什至不知道该从哪里开始。我当前的思路是创建 a …

c++ c++20 std-ranges

6
推荐指数
1
解决办法
546
查看次数

在 C++23 中,仍然无法使用 value_type/reference 为pair的代理迭代器对范围进行排序吗?

尽管P2321zip提供了、 和P2165common_reference之间的专业化类对象之间的兼容性使得和可以相互转换,但 的比较函数在[pairs.spec]中只有以下候选:pairtuplepairtupletuplepairpair

template<class T1, class T2>
  constexpr common_comparison_category_t<...>
    operator<=>(const pair<T1, T2>& x, const pair<T1, T2>& y);
Run Code Online (Sandbox Code Playgroud)

注意到这里只有两个模板参数,所以我们仍然无法比较两个不同的pairs,例如

using value_type = pair<int , string >;
using reference  = pair<int&, string&>;

value_type val = {1, "a"};
reference  ref = val;
val < ref; // no match for 'operator<'
Run Code Online (Sandbox Code Playgroud)

pair这意味着使用as value_type/ 的代理迭代器reference始终无法满足这个sortable …

c++ std-ranges c++23

6
推荐指数
1
解决办法
211
查看次数

就特定视图的使用而言,std::ranges::algorithm 与基于范围的 for() 循环相比在语义上有所不同

我想逐列查看 3x3 网格,所以我想我会std::views::stride像这样使用:

#include <array>
#include <iostream>
#include <ranges>

auto main() -> int {
    auto grid = std::array<std::array<int, 3>, 3>{{
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
    }};

    namespace vs = std::views;

    auto strideCount = grid.size();
    auto allElements = grid | vs::join;

    auto columns = vs::iota(0uz, strideCount)
                   | vs::transform([allElements, strideCount](auto n) {
                        return allElements
                                | vs::drop(n)
                                | vs::stride(strideCount);
                   });

    for (auto&& column : columns) {
        for (auto element : column) {
            std::cout << element << ' …
Run Code Online (Sandbox Code Playgroud)

c++ for-loop std-ranges c++23

6
推荐指数
1
解决办法
301
查看次数

标签 统计

c++ ×10

std-ranges ×10

c++20 ×6

c++23 ×3

range-v3 ×3

for-loop ×1

gcc ×1

performance ×1