---
title: "views::unchecked_(take|drop)"
document: P3230R3
date: 2026-06-13
audience: LEWG, SG9 (Ranges)
reply-to:
  - "Hewill Kang <hewillk@gmail.com>"
---

- Abstract
  - Revision history
  - Discussion
  - Design
  - Implementation experience
  - Benchmarks
  - Proposed change
  - References

## Abstract

This paper proposes two Tier 1 adaptors in [P2760](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html): `views::unchecked_drop` and `views::unchecked_take`, cousins of `drop` and `take`, to improve the C++26 ranges facilities, which were renamed to `views::unchecked_take` and `views::unchecked_drop` after Tokyo WG21, as "unchecked" better describes the utility's nature than "exactly".

## Revision history

### R3

Provided

const begin()

when the underlying range is not a

forward_range

.

Provided

reserve_hint()

.

### R2

Added

Preconditions

to the wording based on SG9 feedback.

Aligned proposed changes with the latest draft.

### R1

Added

unchecked

part for the naming.

Added benchmarks.

Remove

empty_view

specialization for return types.

### R0

Initial revision.

## Discussion

`unchecked_take` and `unchecked_drop` are very similar to `take` and `drop`. The only difference is that the former requires the selection size to be no larger than the size of the original range; otherwise, it is *Undefined Behavior*.

Since there is no boundary check, `views::unchecked_*meow*` is more efficient than `views::*meow*` for situations where the user already knows that there are enough elements in the range, which is what "unchecked" is all about.

For example, for non-sized random-access ranges, the unchecked version can obtain the end/begin position in constant time, while the checked version can only do so in linear time. For non-sized forward ranges, the unchecked version only needs to check if the current count has reached *N* to determine the end, while the checked version additionally compares the current position with the end in each iteration, which is redundant when the input range is large enough.

## Design

### Should we name it views::unchecked_*meow* or views::*meow*_unchecked?

To emphasize the "unchecked" part, it is natural to think about whether to put it before or after "take".

The author prefers to

unchecked_*meow*

to align with the naming convention in the
  current standard, given that
  we already have

inplace_vector::unchecked_push_back

.
  If we put it at the end, it should be more accurate to name it

*meow*_uncheckly

, however,
  "uncheckly" is not quite a common word.

### Should we introduce unchecked_*meow*_view classes?

Although both can achieve similar effects with existing utilities, e.g. `unchecked_take(r, N)` is somewhat equivalent to `views::counted(ranges::begin(r), N)`, and `unchecked_drop` is somewhat equivalent to `subrange(ranges::next(ranges::begin(r), N), ranges::end(r))`, these are not fully replaceable due to certain limitations.

The main problem is that both require manually extracting iterators of the range, and such iterator-based approach causes dangling when applied to rvalue ranges.

Since we've already assumed that the range is large enough, there is no need to consider out-of-bounds cases in the implementation, which means that `unchecked_*meow*_view` is just a simplified version of `*meow*_view`, introducing them doesn't bring much complexity as the `*meow*_view` already not that complicated.

### Should we specialized for return types?

Currently, both `take` and `drop` have optimized the return type. When they take an object of range type `empty_view`, `span`, `string_view`, `subrange`, `iota_view`, and `repeat_view`, it will return an object of the same type to reduce template instantiation. There is no reason not to apply similar optimizations to new ones. Noted that the author removed the specialization for `empty_view` as the value of `N` can only be `0`.

In addition, `views::unchecked_*meow*` applied to infinite ranges now can downgrade to finite ranges. For example, `views::iota(0) | views::unchecked_take(5)` will produce `views::iota(0, 5)`, and `views::iota(0) | views::unchecked_drop(5)` will produce `views::iota(5)`. This also applies to infinite `subrange`s, which can be considered as an improvement.

## Implementation experience

The author implemented `views::unchecked_(take|drop)` based on libstdc++, see [here](https://godbolt.org/z/TraEd54Ge).

## Benchmarks

One of the advantages of `views::unchecked_take` over `views::take` is that it always produces a sized range even if the original range is a non-size range such as `generator` or `istream_view`, this can further benefit the container construction downstream as size information implies the reserve size.

In other words, `r | views::unchecked_take(N) | ranges::to<C>` would have better performance than `r | views::take(N) | ranges::to<C>`. The following table shows that for the input-only range, constructing a `vector` after applying `views::unchecked_take` is ~3.8 times faster than `views::take` in terms of Iterations (see [here](https://godbolt.org/z/hbsffhz8r)):

> | Benchmark | Time | CPU | Iterations |
> | --- | --- | --- | --- |
> | `construct_from_take<std::vector<int>>` | 17454461 ns | 9032396 ns | 59 |
> | `construct_from_unchecked_take<std::vector<int>>` | 7671973 ns | 3164699 ns | 222 |

`views::unchecked_drop` also has better performance when applied to non-sized ranges, as the new `begin()` for all random-access ranges can be obtained in constant time by just calculating `r.begin() + N`. The following table shows that for non-sized null-terminated raw string, constructing a `string` after applying `views::unchecked_drop` is ~1.6 times faster than `views::drop` in terms of Iterations (see [here](https://godbolt.org/z/MEWaoM9nb)):

> | Benchmark | Time | CPU | Iterations |
> | --- | --- | --- | --- |
> | `construct_from_drop<std::string>` | 9762106 ns | 5644617 ns | 121 |
> | `construct_from_unchecked_drop<std::string>` | 6812259 ns | 4149667 ns | 192 |

## Proposed change

This wording is relative to [latest working draft](https://eel.is/c++draft).

## References

[P2760R1]

Barry Revzin. A Plan for C++26 Ranges. URL:

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2760r1.html
