如何在编译时将 std::views::join 结果转换为 string_view?

How to convert std::views::join result to string_view at compile time?

我想让整个代码像 constexpr 一样工作。

这是有效的:

#include <iostream>
#include <ranges>
#include <string_view>

int main() {
    constexpr std::string_view words{"Just some sentence I got from a friend."};
    auto rng = words | std::views::split(' ') | std::views::take(4) | std::views::join;
    std::string_view first_four_words{&*rng.begin(), std::ranges::distance(rng)};
    std::cout << first_four_words << std::endl;
}

但是如果我将 constexpr 添加到 rngfirst_four_words 行,我会得到编译错误。我想也许 cbegin() 会解决它,但我无法用 cbegin 编译它,无论 const 限定符如何,因为它拒绝接受 rng...

那么,有没有办法让它像 constexpr 一样工作?

顺便说一句,有没有更优雅的方法来构建这个 string_view&*特别难看

正确的做法是将其放入函数中,如下所示:

constexpr std::string_view first_n_words(std::string_view str, size_t n) {    
    auto first_n = str
                | rv::split(' ')
                | rv::take(n)
                | rv::join;   

    auto const len = std::min(str.size(), std::ranges::distance(first_n) + n - 1);
    return std::string_view(&*first_n.begin(), len);
}

现在无法编译,因为 gcc 尚未实现 P2231, which is required in order to for join_view to work at compile time, and then libstdc++ needs to change their implementation of emplace_deref to both mark it constexpr (as it is defined to be, filed #102236)。

请注意(正如康桐蔚在评论中指出的那样),std::ranges::distance 是 string_view 的错误长度,因为您没有考虑额外的 space 分隔符被 split 移除。上面的 len 调整应该正确地说明了这一点。


你不能这样做:

constexpr auto rng = words | split(' ') | take(4) | std::views::join;

因为这将是 join 纯右值范围的范围(来自 split),并且这种情况不是 const 可迭代的(因为我们需要缓存每个子- 我们要加入的范围,必须缓存到 join_view 本身,这需要突变,所以我们不能这样做。

生成的对象必须是可变的,这意味着您不能声明它constexpr(这就是为什么我的上述方法在完全实施后应该起作用的原因)。


And as an aside, is there a more elegant way to build this string_view? The &* is particularly ugly.

不是真的。为了 views::join 能够可靠地给你一个连续的范围,仅仅知道我们 join 是一个连续的范围是不够的,我们还必须知道那些连续的部分是他们自己是连续的。而且......他们不是在这种情况下(因为我们正在删除 space 分隔符)。即使有一些机制来保持连续性(我不知道这样的机制会是什么样子),这甚至不是应用它的有效地方——我们不是这里有一个连续的范围。

但在这种情况下,您知道这些部分恰好是连续的,因为您知道我们如何构建此管道的所有信息,并且这是一个安全的操作,因为您知道您想要保持在space 分隔您的连续子范围。但是你要超出这里的范围模型并做一些可能不安全的事情,所以这会有点难看。