标签: 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++20 中的 iterator_category 和 iterator_concept 有什么区别?

C++20带来了更强大的迭代器系统,其中之一就是iterator_conceptiterator_category.

我发现C++20 中很多迭代器的iterator_conceptiterator_category是不一致的。以最著名iota_view为例

using R = decltype(views::iota(0));
static_assert(random_access_range<R>);

using I = ranges::iterator_t<R>;
static_assert(same_as<typename I::iterator_category, input_iterator_tag>);
static_assert(same_as<typename I::iterator_concept,  random_access_iterator_tag>);
Run Code Online (Sandbox Code Playgroud)

虽然是Rmodels random_access_range,但iterator_category它的迭代器的the只是一个input_iterator_tag,与iterator_concept.

为什么C++20引入iterator_concept?它的目的是什么?如果我实现我自己的迭代器,我该如何界定iterator_conceptiterator_category正确?是否iterator_category仍然在C ++ 20的意思?

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

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

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
查看次数

为什么使用 ADL 时 `std::ranges::size` 需要非常量方法?

否则,如果 Ranges::disable_sized_range<std::remove_cv_t<T>> 为 false,则 size(t) 转换为其衰减类型,并且转换后的表达式有效且具有类似整数的类型,其中重载解析通过以下命令执行以下候选人:

  • void size(auto&) = delete;
  • void size(const auto&) = delete; 1
class Test {
    friend size_t size(/*const*/ Test&) {
        return 0;
    }
};

int main() {
    std::ranges::size(Test{});
    // no matching function error when adding the `const` qualifier
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/79e5vrKrT


一般来说,方法size不需要std::size.

为什么会有这样的限制呢std::ranges::size?(好像只有非会员版才能执行。)

c++ argument-dependent-lookup c++20 std-ranges

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

为什么我的函数只能使用左值?

我有一个返回小写字符串的函数:

constexpr auto string_to_lower_case(const std::string& string) {
    return string
        | std::views::transform(std::tolower)
        | std::views::transform([](const auto& ascii) { return static_cast<char>(ascii); });
}
Run Code Online (Sandbox Code Playgroud)

我希望当我传递"SOME"or时该函数返回相同的结果const std::string some("SOME"),但事实并非如此。当我尝试打印 的结果时string_to_lower_case("SOME"),我检索到一个空控制台( 的输出string_to_lower_case(some)是正确的)

const std::string some("SOME");
for (const auto& ch : string_to_lower_case(some))
    std::cout << ch;
Run Code Online (Sandbox Code Playgroud)

c++ string rvalue lvalue std-ranges

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

关于 MSVC std::experimental::generator 中的暂停-恢复数据丢失

自从std::generator正在进入 CPP23,我正在尝试 MSVC 的不完整版本。

然而,我注意到,yield与 一起使用时,它似乎恰好丢失了一个std::views::take。这是示例:

#include <iostream>
#include <ranges>

#include <experimental/generator>

std::experimental::generator<int> GeneratorFn(void) noexcept
{
    co_yield 1;
    co_yield 2;
    co_yield 3;
    co_yield 4;
    co_yield 5;
    co_yield 6;
    co_yield 7;
    co_yield 8;
    co_yield 9;
    co_return;
}

int main(int argc, char** args) noexcept
{
    auto Ret = GeneratorFn();
    for (auto&& i : Ret | std::views::take(2))
        std::cout << i << '\n';
    for (auto&& i : Ret | std::views::take(3))
        std::cout << i << '\n';
    for (auto&& i …
Run Code Online (Sandbox Code Playgroud)

c++ generator c++-coroutine std-ranges c++23

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

lambda 和成员函数指针的区别

在我的回答中巴里指出最好打电话,views::transform(&Planter::getPlants)因为views::transform([](Planter const& planter){...不小心复制了。

#if 1
    auto plants = planters
        | std::views::transform([](Planter const& planter){ return planter.getPlants();})
        | std::views::join
        | std::views::common
        ;
// Plant copy constructor
// Plant copy constructor
// Plant copy constructor
// Plant copy constructor
// Plant copy constructor
#else
    auto plants = planters
        | std::views::transform(&Planter::getPlants)
        | std::views::join
        ;
#endif
// Plant copy constructor
// Plant copy constructor
Run Code Online (Sandbox Code Playgroud)

Plant是一个围绕的包装器intPlanter也是一个围绕的包装器std::vector<int>

https://godbolt.org/z/dr7PM5Tvd

c++ std-ranges

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

有没有办法确定范围是否正确构建?

boost::join在混合 的输出时,我遇到了意外的行为std::views::transform(如下所示)。编译器不会发出任何警告。幸运的是,地址清理程序可以检测到get2b().

get1b()函数遵循与 相同的模式get2b(),但该代码工作正常。考虑到 UB 的可能性,我如何确定构造的范围是合法的?我偏执的一面想get1b()写成return std::move(rng) | ...

https://www.godbolt.org/z/Y77YW3jYb

#include <array>
#include <ranges>
#include <algorithm>
#include <iostream>
#include <iterator>
#include "boost/range/join.hpp"
#include "boost/range/adaptor/transformed.hpp"

inline auto square = [](int x) { return x*x; };

struct A {
    std::array<std::vector<int>, 3> m_data{ std::vector{1, 2}, std::vector{3, 4, 5}, std::vector{6} };
    auto join1() const { return m_data | std::views::join; }
    auto join2() const { return boost::join(m_data[0], boost::join(m_data[1], m_data[2])); }
    auto get1a() …
Run Code Online (Sandbox Code Playgroud)

c++ boost c++20 std-ranges

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

使用 C++23 zip 视图对两个数组进行排序

有一个相当典型的任务是同时对两个数组进行排序,假设数组的相同索引元素形成虚拟对,并对其进行排序。这样的问题至少在 10 年前就出现过:boost zip_iterator 和 std::sort

现在这个任务可以使用range-v3库来解决:

#include <array>
#include <range/v3/all.hpp>

int main() {
   auto x = std::array{ 3,   2,   4,   1 };
   auto y = std::array{'A', 'B', 'C', 'D'};
   ranges::sort( ranges::views::zip( x, y ) );
   // here x = {1,2,3,4}, y={'D','B','A','C'}
}
Run Code Online (Sandbox Code Playgroud)

在线演示: https: //gcc.godbolt.org/z/WGo4vGsx5

在 C++23 中出现了std::ranges::zip_view,我的期望是可以仅使用标准库编写相同的程序:

#include <array>
#include <ranges>
#include <algorithm>

int main() {
   auto x = std::array{ 3,   2,   4,   1 };
   auto y = std::array{'A', 'B', 'C', 'D'};
   std::ranges::sort( std::views::zip( …
Run Code Online (Sandbox Code Playgroud)

c++ std-ranges c++23

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

在“ranges::to”中使用别名容器是否有效?

目前只有 MSVC 支持 nifty helper ranges::to,所以我无法在另一个编译器中验证这一点。基本上,我有一个 STL 容器的类型别名,一旦我尝试将其传递给ranges::to,编译就会失败。那么这是有效的用法吗?为什么下面的第二个(注释掉的)示例无法编译?

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

template <typename T>
using Vec = std::vector<T>;

int main(int argc, char* argv[]) {
    auto vec = std::views::iota(1, 10) | std::ranges::to<std::vector>();
    //auto vec = std::views::iota(1, 10) | std::ranges::to<Vec>(); // C2440: cannot convert from 'void' to 'std::vector'.

    for (auto& v : vec)
        std::cout << v << ", ";

    std::cout << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

c++ visual-c++ std-ranges c++23

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