在一个简单的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) 我已经阅读了最新的草案,其中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) 我无法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)
我发现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) 如何使用 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++ stl 算法的传统语法很烦人;使用它们编写起来很长只是一个小问题,但它们总是需要对现有对象进行操作,这在很大程度上限制了它们的可组合性。
我很高兴看到 stl 中范围的出现;然而,从 C++20 开始,存在严重的缺点:标准库的不同实现对此的支持各不相同,并且 range-v3 中存在的许多内容并未进入 C++20,例如(对我来说)非常令人惊讶),将视图转换为向量(对我来说,如果我无法将计算结果存储在向量中,这会使这一切变得毫无用处)。
另一方面,使用 range-v3 对我来说似乎也不理想:它的文档很少(而且我不同意其中的所有内容都是不言自明的),而且更严重的是,C++20 的想法range 与 range-v3 所做的不同,所以我不能只是说,好吧,让我们坚持使用 range-v3;无论如何,这将在某个时候成为标准。
那么,我应该使用两者中的任何一个吗?或者这一切都不值得,并且通过依赖 std 范围或 range-v3,使我的代码太难以维护和移植?
我最近一直在对范围和视图进行一些性能评估。我发布了一个简单的示例(也在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) 我被分配了一项任务,需要在考虑到几个限制的情况下解决问题。重点是强制使用 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_eachstd::vector我的教授声称“c++20 范围使这项任务特别简单”。但即使在阅读了几个小时的范围后,我什至不知道该从哪里开始。我当前的思路是创建 a …
尽管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 …
我想逐列查看 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)