使用 c++20 范围删除最后一个元素的最佳方法是什么
What is the best way to drop last element using c++20 ranges
有没有比反转两次更好的方法来使用 c++20 范围将最后一个元素放入容器中?
#include <iostream>
#include <vector>
#include <ranges>
int main()
{
std::vector<int> foo{1, 2, 3, 4, 5, 6};
for (const auto& d: foo | std::ranges::views::reverse
| std::ranges::views::drop(1)
| std::ranges::views::reverse)
{
std::cout << d << std::endl;
}
}
您需要的是 views::drop_last
,它来自 p2214,优先级为第 2 层。
作为 paper says:
We’ll go through the other potential range adapters in this family and
discuss how they could be implemented in terms of existing adapters:
take_last(N)
and drop_last(N)
. views::take_last(N)
is equivalent to
views::reverse | views::take(N) | views::reverse
. But this is somewhat
expensive, especially for non-common views. For random-access, sized
ranges, we’re probably want r | views::take_last(N)
to evaluate as r | views::drop(r.size() - N)
, and that desire is really the crux of this
whole question — is the equivalent version good enough or should we
want to do it right?
因为 vector
是一个 random-access,大小范围,你可以做
for (const auto& d: foo | std::views::take(foo.size() - 1))
{
std::cout << d << std::endl;
}
使用span
怎么样?
#include <iostream>
#include <span>
#include <vector>
int main() {
std::vector<int> foo{1, 2, 3, 4, 5, 6};
for (const auto& d : std::span(foo.begin(), foo.end() - 1)) {
std::cout << d << '\n';
}
}
您可以通过以下方式进行很好的近似:
struct drop_last_t {
template <std::ranges::sized_range R>
requires std::ranges::viewable_range<R>
friend auto operator|(R&& r, drop_last_t) {
auto n = std::ranges::size(r);
return std::views::take(std::forward<R>(r), n > 0 ? n - 1 : 0);
}
};
inline constexpr drop_last_t drop_last;
那个lets you:
for (const auto& d: foo | drop_last)
这不是一个完美的范围适配器,因为你不能写类似 auto my_adaptor = transform(f) | drop_last;
的东西为了做到这一点,你需要 P2387,这是一个 C++23 库特性.在 C++23 中,你可以这样写:
struct drop_last_t : std::ranges::range_adaptor_closure<drop_last_t>
{
template <std::ranges::sized_range R>
requires std::ranges::viewable_range<R>
auto operator()(R&& r) const {
auto n = std::ranges::size(r);
return std::views::take(std::forward<R>(r), n > 0 ? n - 1 : 0);
}
};
inline constexpr drop_last_t drop_last;
现在这是一个功能齐全的范围适配器。当前特定于 libstdc++ 的版本看起来 like this(只是为了演示,实际上不要这样做——这不是你在 C++23 中这样做的方式)。
当然,这仅限于大小范围。这有各种各样的方向。您可以通过执行 std::ranges::distance(r)
来支持任何前向范围(以多次遍历为代价)。但是定制的实现可以做得更好。对于bidi+common,你只需要停在prev(end(r))
。对于仅向前,您可以一次推进两个迭代器等。只是考虑一下。
有没有比反转两次更好的方法来使用 c++20 范围将最后一个元素放入容器中?
#include <iostream>
#include <vector>
#include <ranges>
int main()
{
std::vector<int> foo{1, 2, 3, 4, 5, 6};
for (const auto& d: foo | std::ranges::views::reverse
| std::ranges::views::drop(1)
| std::ranges::views::reverse)
{
std::cout << d << std::endl;
}
}
您需要的是 views::drop_last
,它来自 p2214,优先级为第 2 层。
作为 paper says:
We’ll go through the other potential range adapters in this family and discuss how they could be implemented in terms of existing adapters:
take_last(N)
anddrop_last(N)
.views::take_last(N)
is equivalent toviews::reverse | views::take(N) | views::reverse
. But this is somewhat expensive, especially for non-common views. For random-access, sized ranges, we’re probably wantr | views::take_last(N)
to evaluate asr | views::drop(r.size() - N)
, and that desire is really the crux of this whole question — is the equivalent version good enough or should we want to do it right?
因为 vector
是一个 random-access,大小范围,你可以做
for (const auto& d: foo | std::views::take(foo.size() - 1))
{
std::cout << d << std::endl;
}
使用span
怎么样?
#include <iostream>
#include <span>
#include <vector>
int main() {
std::vector<int> foo{1, 2, 3, 4, 5, 6};
for (const auto& d : std::span(foo.begin(), foo.end() - 1)) {
std::cout << d << '\n';
}
}
您可以通过以下方式进行很好的近似:
struct drop_last_t {
template <std::ranges::sized_range R>
requires std::ranges::viewable_range<R>
friend auto operator|(R&& r, drop_last_t) {
auto n = std::ranges::size(r);
return std::views::take(std::forward<R>(r), n > 0 ? n - 1 : 0);
}
};
inline constexpr drop_last_t drop_last;
那个lets you:
for (const auto& d: foo | drop_last)
这不是一个完美的范围适配器,因为你不能写类似 auto my_adaptor = transform(f) | drop_last;
的东西为了做到这一点,你需要 P2387,这是一个 C++23 库特性.在 C++23 中,你可以这样写:
struct drop_last_t : std::ranges::range_adaptor_closure<drop_last_t>
{
template <std::ranges::sized_range R>
requires std::ranges::viewable_range<R>
auto operator()(R&& r) const {
auto n = std::ranges::size(r);
return std::views::take(std::forward<R>(r), n > 0 ? n - 1 : 0);
}
};
inline constexpr drop_last_t drop_last;
现在这是一个功能齐全的范围适配器。当前特定于 libstdc++ 的版本看起来 like this(只是为了演示,实际上不要这样做——这不是你在 C++23 中这样做的方式)。
当然,这仅限于大小范围。这有各种各样的方向。您可以通过执行 std::ranges::distance(r)
来支持任何前向范围(以多次遍历为代价)。但是定制的实现可以做得更好。对于bidi+common,你只需要停在prev(end(r))
。对于仅向前,您可以一次推进两个迭代器等。只是考虑一下。