如何在编译时将 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
添加到 rng
和 first_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 分隔您的连续子范围。但是你要超出这里的范围模型并做一些可能不安全的事情,所以这会有点难看。
我想让整个代码像 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
添加到 rng
和 first_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 分隔您的连续子范围。但是你要超出这里的范围模型并做一些可能不安全的事情,所以这会有点难看。