C++26 中连接或合并范围的方法
How to join or concat ranges, C++26

原始链接: https://www.cppstories.com/2025/join_concat_ranges/

## 现代 C++ 范围:连接和扁平化集合 现代 C++ 的范围库提供了强大的集合操作工具,超越了传统的循环和复制,实现了高效的延迟求值。三个关键适配器——`concat_view` (C++26)、`join_view` (C++20) 和 `join_with_view` (C++23)——为组合范围提供了优雅的解决方案。 `concat_view` 将多个*独立*的范围合并成一个序列,如果底层范围允许,则支持随机访问和修改。`join_view` 将一个范围的范围(例如向量的向量)扁平化成一个,消除了结构边界,但缺乏随机访问。最后,`join_with_view` 通过允许在扁平化的子范围之间插入分隔符来扩展 `join_view`。 **快速指南:** * **`concat_view`**: 组合独立的范围。 * **`join_view`**: 扁平化嵌套的范围。 * **`join_with_view`**: 扁平化嵌套的范围*并*添加分隔符。 这些视图避免了不必要的内存分配,提高了性能和代码可读性,从而改善了在 C++ 中处理集合的方式。它们代表了 C++ 编程表达性和效率方面的重要进步。

黑客新闻 新 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 如何连接或合并范围,C++26 (cppstories.com) 7 分,ibobev 发表于 1 小时前 | 隐藏 | 过去 | 收藏 | 讨论 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

Modern C++ continuously improves its range library to provide more expressive, flexible, and efficient ways to manipulate collections. Traditionally, achieving tasks like concatenation and flattening required manual loops, copying, or custom algorithms. With C++’s range adaptors, we now have an elegant and efficient way to process collections lazily without unnecessary allocations.

In this post, we will explore three powerful range adaptors introduced in different C++ standards:

  • std::ranges::concat_view (C++26)
  • std::ranges::join_view (C++20)
  • std::ranges::join_with_view (C++23)

Let’s break down their differences, use cases, and examples.

std::ranges::concat_view (C++26)  

The concat_view allows you to concatenate multiple independent ranges into a single sequence. Unlike join_view, it does not require a range of ranges—it simply merges the given ranges sequentially while preserving their structure.

In short:

  • Takes multiple independent ranges as arguments.
  • Supports random access if all underlying ranges support it.
  • Allows modification if underlying ranges are writable.
  • Lazy evaluation: No additional memory allocations.

See the example:

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

int main() {
    std::vector<std::string> v1{"world", "hi"};
    std::vector<std::string> v2 { "abc", "xyz" };
    std::string arr[]{"one", "two", "three"};
    
    auto v1_rev = v1 | std::views::reverse;
    auto concat = std::views::concat(v1_rev, v2, arr);

    concat[0] = "hello"; // access and write

    for (auto& elem : concat)
        std::print("{} ", elem);
}

See at @Compiler Explorer

hello world abc xyz one two three 

The example below shows how to concatenate three ranges, v1 is reversed and then combined with v2 and arr. Notice that we can also access the value at position 0 and update it.

And a bit more complex example:

#include <print>
#include <ranges>
#include <vector>
#include <list>
#include <string>

struct Transaction {
    std::string type;
    double amount;
};

int main() {
    std::vector<Transaction> bank_transactions{
       {"Deposit", 100.0}, 
       {"Withdraw", 50.0}, 
       {"Deposit", 200.0}
    };
    std::list<Transaction> credit_card_transactions{
        {"Charge", 75.0}, {"Payment", 50.0}
    };
    
    auto filtered_bank = bank_transactions 
        | std::views::filter([](const Transaction& t) {
        return t.amount >= 100.0;
    });
    
    auto filtered_credit = credit_card_transactions 
        | std::views::filter([](const Transaction& t) {
        return t.amount > 60.0;
    });
    
    auto all_transactions = std::views::concat(filtered_bank, filtered_credit);
    
    for (const auto& t : all_transactions)
        std::println("{} - {}$", t.type, t.amount);
}

Run @Compiler Explorer

std::ranges::join_view (C++20)  

The join_view is designed for flattening a single range of ranges into a single sequence. It removes the structural boundaries between nested ranges.

  • Works on a single range of ranges (e.g., std::vector<std::vector<int>>).
  • Does not support operator[] (no random access).
  • Eliminates boundaries between sub-ranges.
  • Lazy evaluation, avoiding memory copies.

A simple example:

#include <iostream>
#include <ranges>
#include <vector>

int main() {
    std::vector<std::vector<int>> nested{{1, 2}, {3, 4, 5}, {6, 7}};
    auto joined = std::views::join(nested);
    
    for (int i : joined) 
        std::println(i);
}

Run @Compiler Explorer

1 2 3 4 5 6 7

Of course, we can have different nested ranges… and this can be handy for string processing:

#include <print>
#include <ranges>
#include <vector>
#include <string>
#include <map>

int main() {
    std::vector<std::string> words { "Hello", "World", "Coding" };

    // regular:
    std::map<char, int> freq;
    for (auto &w : words)
        for (auto &c : w)
            freq[::tolower(c)]++;

    // join:
    std::map<char, int> freq2;
    for (auto &c : words | std::views::join)
        freq2[::tolower(c)]++;

    for (auto& [key, val] : freq2)
        std::println("{} -> {}", key, val);
}

Run @Compiler Explorer

As you can see, thanks to views_join we can save one nested loop and iterate through a single range of characters.

std::ranges::join_with_view (C++23)  

The join_with_view works like join_view, but it allows inserting a delimiter between flattened sub-ranges.

  • Works on a single range of ranges.
  • Allows specifying a delimiter (single element or a range).
  • Does not support random access.
  • Useful for formatting strings or separating collections.

See the example below:

#include <iostream>
#include <ranges>
#include <vector>
#include <string>
#include <string_view>

std::string to_uppercase(std::string_view word) {
    std::string result(word);
    for (char& c : result) 
        c = std::toupper(static_cast<unsigned char>(c));
    return result;
}

int main() {
    std::vector<std::string_view> words{
        "The", "C++", "ranges", "library"
    };

    auto words_up = words | std::views::transform(to_uppercase);
    auto joined = std::views::join_with(words_up, std::string_view(" "));

    for (auto c : joined) 
        std::cout << c;
}

See at Compiler Explorer

THE C++ RANGES LIBRARY

Comparing concat_view, join_view, and join_with_view  

Feature concat_view join_view join_with_view
Works on multiple independent ranges?
Flattens nested ranges?
Supports separators between sub-ranges?
Random access support? ✅ (if all inputs support it)

Summary  

C++’s range adaptors provide efficient ways to manipulate collections without unnecessary copying. Here’s a quick summary of when to use each view:

  • Use concat_view when merging multiple independent ranges.
  • Use join_view when flattening a range of ranges.
  • Use join_with_view when flattening a range of ranges but needing a separator between elements.

Back to you

  • Do you use ranges?
  • What are your most useful views ald algorithms on ranges?

Share your comments below

联系我们 contact @ memedata.com