Skip to content

P3059R2: Making user-defined constructors of view iterators/sentinels private. - #6382

Draft
iskandarem wants to merge 25 commits into
microsoft:mainfrom
iskandarem:unable-view-sentinel-iterator-creation-from-outside
Draft

P3059R2: Making user-defined constructors of view iterators/sentinels private. #6382
iskandarem wants to merge 25 commits into
microsoft:mainfrom
iskandarem:unable-view-sentinel-iterator-creation-from-outside

Conversation

@iskandarem

@iskandarem iskandarem commented Jul 26, 2026

Copy link
Copy Markdown
Contributor
  • User defined constructors following view-iterators/sentinels made private:
    • filter_view::iterator/sentinel
    • iota_view::_Ioterator
    • basic_istream_view::iterator
    • transform_view::iterator/sentinel
    • take_view::sentinel
    • take_while_view::sentinel
    • join_view::sentinel
    • lazy_split_view::outer_iterator
    • lazy_split_view::inner_iterator
    • split_view::iterator/sentinel
    • element_view::iterator/sentinel
    • iota_view::_Iotinel
  • Tests are written in following folders (for tests AI was used)
    • P3059R2_basic_istream_view_iterator_constructor
    • P3059R2_elements_view_iterator_sentinel_constructors
    • P3059R2_filter_view_iterator_sentinel_constructors
    • P3059R2_iota_view_iterator_sentinel_construtors
    • P3059R2_join_view_sentinel_constructor
    • P3059R2_lazy_split_view_inner_outer_iterators_constructors
    • P3059R2_split_view_iterator_sentinel_constructors
    • P3059R2_take_view_sentinel_constructor
    • P3059R2_take_while_view_sentinel_constructor
    • P3059R2_transform_view_iterator_sentinel_constructors

closes #6241

Copilot AI review requested due to automatic review settings July 26, 2026 18:06
@iskandarem
iskandarem requested a review from a team as a code owner July 26, 2026 18:06
@github-project-automation github-project-automation Bot moved this to Initial Review in STL Code Reviews Jul 26, 2026
@azure-pipelines

This comment was marked as resolved.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements P3059R2 by making internal view iterator and sentinel constructors private while preserving required public operations.

Changes:

  • Restricts iterator and sentinel construction across affected C++20 views.
  • Adds compile-time tests for constructor accessibility and public interfaces.
  • Registers the new tests in the STL test suite.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
stl/inc/ranges Restricts constructors for affected range views.
stl/inc/__msvc_ranges_to.hpp Restricts transform_view constructors.
tests/std/test.lst Registers the new tests.
tests/std/tests/P3059R2_basic_istream_view_iterator_constructor/test.compile.pass.cpp Tests basic_istream_view.
tests/std/tests/P3059R2_basic_istream_view_iterator_constructor/env.lst Configures the test matrix.
tests/std/tests/P3059R2_elements_view_iterator_sentinel_constructors/test.compile.pass.cpp Tests elements_view.
tests/std/tests/P3059R2_elements_view_iterator_sentinel_constructors/env.lst Configures the test matrix.
tests/std/tests/P3059R2_filter_view_iterator_sentinel_constructors/test.compile.pass.cpp Tests filter_view.
tests/std/tests/P3059R2_filter_view_iterator_sentinel_constructors/env.lst Configures the test matrix.
tests/std/tests/P3059R2_iota_view_iterator_sentinel_construtors/test.compile.pass.cpp Tests iota_view.
tests/std/tests/P3059R2_iota_view_iterator_sentinel_construtors/env.lst Configures the test matrix.
tests/std/tests/P3059R2_join_view_sentinel_constructor/test.compile.pass.cpp Tests join_view.
tests/std/tests/P3059R2_join_view_sentinel_constructor/env.lst Configures the test matrix.
tests/std/tests/P3059R2_lazy_split_view_inner_outer_iterators_constructors/test.compile.pass.cpp Tests lazy_split_view.
tests/std/tests/P3059R2_lazy_split_view_inner_outer_iterators_constructors/env.lst Configures the test matrix.
tests/std/tests/P3059R2_split_view_iterator_sentinel_constructors/test.compile.pass.cpp Tests split_view.
tests/std/tests/P3059R2_split_view_iterator_sentinel_constructors/env.lst Configures the test matrix.
tests/std/tests/P3059R2_take_view_sentinel_constructor/test.compile.pass.cpp Tests take_view.
tests/std/tests/P3059R2_take_view_sentinel_constructor/env.lst Configures the test matrix.
tests/std/tests/P3059R2_take_while_view_sentinel_constructor/test.compile.pass.cpp Tests take_while_view.
tests/std/tests/P3059R2_take_while_view_sentinel_constructor/env.lst Configures the test matrix.
tests/std/tests/P3059R2_transform_view_iterator_sentinel_constructors/test.compile.pass.cpp Tests transform_view.
tests/std/tests/P3059R2_transform_view_iterator_sentinel_constructors/env.lst Configures the test matrix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread stl/inc/ranges
}
}

friend lazy_split_view;
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_latest_matrix.lst
Comment thread tests/std/tests/P3059R2_take_while_view_sentinel_constructor/env.lst Outdated
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_latest_matrix.lst
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_latest_matrix.lst
Comment thread tests/std/tests/P3059R2_filter_view_iterator_sentinel_constructors/env.lst Outdated
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_latest_matrix.lst No newline at end of file
@@ -0,0 +1 @@
RUNALL_INCLUDE ..\usual_latest_matrix.lst No newline at end of file
// basic_istream_view::iterator directly from the parent view.
static_assert(!constructible_from<Iterator, View&>);

// The iterator must remain copyable and movable through its public interface.
Comment thread tests/std/test.lst Outdated
tests\P3059R2_basic_istream_view_iterator_constructor
tests\P3059R2_elements_view_iterator_sentinel_constructors
tests\P3059R2_filter_view_iterator_sentinel_constructors
tests\P3059R2_iota_view_iterator_sentinel_construtors
@StephanTLavavej StephanTLavavej moved this from Initial Review to Work In Progress in STL Code Reviews Jul 26, 2026
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 26, 2026 18:14
@StephanTLavavej StephanTLavavej added cxx20 C++20 feature ranges C++20/23 ranges defect report Applied retroactively labels Jul 26, 2026
@StephanTLavavej

Copy link
Copy Markdown
Member

Please don't just click "Commit suggestion" on Copilot code review comments. That will immediately push a commit, rerunning checks. (I don't personally pay for them, but they're a shared resource, and if we waste it enough, someone will eventually hiss at me.) In this case, there were several code review comments from Copilot, and your checks were already failing in Code Format Validation. The proper thing to do is to locally address all feedback, from both the failed checks and Copilot, then validate and push a single time. (You can push one or multiple commits at once.)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (11)

tests/std/tests/P3059R2_basic_istream_view_iterator_constructor/test.compile.pass.cpp:17

  • This comment contradicts the assertion immediately below: the iterator is intentionally not copy-constructible. Describe the preserved move-only interface instead.
// The iterator must remain copyable and movable through its public interface.

tests/std/test.lst:742

  • The new test directory misspells “constructors” as “construtors”. Rename the directory and update this registration to keep the test name consistent with the other P3059R2 tests.
tests\P3059R2_iota_view_iterator_sentinel_construtors

tests/std/tests/P3059R2_transform_view_iterator_sentinel_constructors/env.lst:4

  • P3059R2 was adopted as a defect report retroactive to C++20, but usual_latest_matrix.lst only compiles this test with /std:c++latest. Use the C++20 matrix so the required C++20 behavior is covered as well.
RUNALL_INCLUDE ..\usual_latest_matrix.lst

tests/std/tests/P3059R2_take_view_sentinel_constructor/env.lst:4

  • P3059R2 was adopted as a defect report retroactive to C++20, but usual_latest_matrix.lst only compiles this test with /std:c++latest. Use the C++20 matrix so the required C++20 behavior is covered as well.
RUNALL_INCLUDE ..\usual_latest_matrix.lst

tests/std/tests/P3059R2_split_view_iterator_sentinel_constructors/env.lst:4

  • P3059R2 was adopted as a defect report retroactive to C++20, but usual_latest_matrix.lst only compiles this test with /std:c++latest. Use the C++20 matrix so the required C++20 behavior is covered as well.
RUNALL_INCLUDE ..\usual_latest_matrix.lst

tests/std/tests/P3059R2_lazy_split_view_inner_outer_iterators_constructors/env.lst:4

  • P3059R2 was adopted as a defect report retroactive to C++20, but usual_latest_matrix.lst only compiles this test with /std:c++latest. Use the C++20 matrix so the required C++20 behavior is covered as well.
RUNALL_INCLUDE ..\usual_latest_matrix.lst

tests/std/tests/P3059R2_join_view_sentinel_constructor/env.lst:4

  • P3059R2 was adopted as a defect report retroactive to C++20, but usual_latest_matrix.lst only compiles this test with /std:c++latest. Use the C++20 matrix so the required C++20 behavior is covered as well.
RUNALL_INCLUDE ..\usual_latest_matrix.lst

tests/std/tests/P3059R2_iota_view_iterator_sentinel_construtors/env.lst:4

  • P3059R2 was adopted as a defect report retroactive to C++20, but usual_latest_matrix.lst only compiles this test with /std:c++latest. Use the C++20 matrix so the required C++20 behavior is covered as well.
RUNALL_INCLUDE ..\usual_latest_matrix.lst

tests/std/tests/P3059R2_filter_view_iterator_sentinel_constructors/env.lst:1

  • P3059R2 was adopted as a defect report retroactive to C++20, but usual_latest_matrix.lst only compiles this test with /std:c++latest. Use the C++20 matrix so the required C++20 behavior is covered as well.
RUNALL_INCLUDE ..\usual_latest_matrix.lst

tests/std/tests/P3059R2_elements_view_iterator_sentinel_constructors/env.lst:4

  • P3059R2 was adopted as a defect report retroactive to C++20, but usual_latest_matrix.lst only compiles this test with /std:c++latest. Use the C++20 matrix so the required C++20 behavior is covered as well.
RUNALL_INCLUDE ..\usual_latest_matrix.lst

tests/std/tests/P3059R2_basic_istream_view_iterator_constructor/env.lst:1

  • P3059R2 was adopted as a defect report retroactive to C++20, but usual_latest_matrix.lst only compiles this test with /std:c++latest. Use the C++20 matrix so the required C++20 behavior is covered as well.
RUNALL_INCLUDE ..\usual_latest_matrix.lst

Comment thread stl/inc/ranges Outdated
_Iterator()
requires default_initializable<iterator_t<_Vw>>
= default;
friend filter_view; // to so that filter_view had access to the private constructor

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Copilot here (except that it wants to mess up the indentation, lol). In general, inspect similar code and see if we think something is subtle enough to deserve a comment.

@iskandarem

Copy link
Copy Markdown
Contributor Author

Please don't just click "Commit suggestion" on Copilot code review comments. That will immediately push a commit, rerunning checks. (I don't personally pay for them, but they're a shared resource, and if we waste it enough, someone will eventually hiss at me.) In this case, there were several code review comments from Copilot, and your checks were already failing in Code Format Validation. The proper thing to do is to locally address all feedback, from both the failed checks and Copilot, then validate and push a single time. (You can push one or multiple commits at once.)

Sorry, I didn't know about that. I'll pay attention in the future.

@iskandarem
iskandarem marked this pull request as draft July 26, 2026 18:50
@StephanTLavavej

Copy link
Copy Markdown
Member

No worries, I still need to write up Contribution Guidelines that will explain all of this stuff.

While your PR is in draft state, pushes won't trigger checks. When you want checks to run again, move your PR back to ready to review, then push commits. If you do the reverse (push then move to ready), the state change of moving out of draft mode won't trigger checks.

@iskandarem
iskandarem marked this pull request as ready for review July 27, 2026 21:55
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI review requested due to automatic review settings July 27, 2026 21:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.

// const sentinel specialization must remain available.
static_assert(constructible_from<ConstSentinel, Sentinel>);

// A non-common underlying range causes take_view to use its sentinel type.
static_assert(constructible_from<ConstSentinel, Sentinel>);


// The outer range is non-common, causing join_view to use its sentinel type.
static_assert(test_non_common_filter_view());


// Use a non-common underlying range so filter_view has a distinct sentinel.
Copilot AI review requested due to automatic review settings July 27, 2026 23:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (2)

tests/std/tests/P3059R2_take_view_sentinel_constructor/test.compile.pass.cpp:50

  • Base is a simple view, so the non-const take_view overload is disabled and both aliases resolve to _Sentinel<true>. This assertion therefore checks the copy constructor rather than the required <false>-to-<true> conversion, and _Sentinel<false> is never exercised. Use a non-simple base with distinct const/non-const range types and assert that Sentinel and ConstSentinel differ before checking conversion.
// The public conversion from the non-const sentinel specialization to the
// const sentinel specialization must remain available.
static_assert(constructible_from<ConstSentinel, Sentinel>);

tests/std/tests/P3059R2_join_view_sentinel_constructor/test.compile.pass.cpp:50

  • The chosen subrange is a simple view, and join_view::end() therefore already returns _Sentinel<true> for non-const View. Sentinel and ConstSentinel are identical, so this assertion tests copying instead of the converting constructor and never covers _Sentinel<false>. Use a non-simple outer range and assert the two aliases differ before this check.
// The public conversion from sentinel<false> to sentinel<true> must remain
// available.
static_assert(constructible_from<ConstSentinel, Sentinel>);

Comment on lines +52 to +54
// The public converting constructor from sentinel<false> to sentinel<true>
// must remain available.
static_assert(constructible_from<ConstSentinel, Sentinel>);
Comment on lines +59 to +61
// The public conversion from outer-iterator<false> to outer-iterator<true>
// remains available.
static_assert(constructible_from<ConstForwardOuter, ForwardOuter>);
Comment on lines +74 to +77
// The public non-const-to-const converting constructors must remain
// available.
static_assert(constructible_from<ConstIterator, Iterator>);
static_assert(constructible_from<ConstSentinel, Sentinel>);
Copilot AI review requested due to automatic review settings July 27, 2026 23:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.

Comment thread stl/inc/ranges
Comment on lines +918 to +921
/* [[no_unique_address]] */ _Bo _Last{};

constexpr explicit _Iotinel(_Bo _Last_) noexcept(is_nothrow_move_constructible_v<_Bo>)
: _Last(_STD move(_Last_)) {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this PR changes _Iotinel from aggregate to non-aggregate, which may cause ABI change for certain underlying bound types. Is this acceptable?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right, @frederick-vs-ja. However, in the paper I found the following example:

namespace std::ranges {
    template<weakly_incrementable W, semiregular Bound>
        requires weakly-equality-comparable-with<W, Bound> && copyable<W>
    struct iota_view<W, Bound>::iterator {
    private:
        W value_ = W();                       // exposition only

        constexpr explicit iterator(W value); // exposition only

    public:
        // ...
        iterator() requires default_initializable<W> = default;
        // ...
    };
}

Based on that wording, I thought the closest implementation would be to make the stored bound and the constructor private, matching the paper's exposition as closely as possible.

I'd also be interested to know what the MSVC STL policy is regarding ABI changes in cases like this

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C++23 ABI is not yet finalized so we can change it arbitrarily.

The C++20 ABI is finalized so we need to avoid breaking it. That said, if this is a Standard-mandated change, and restricted to iota_view sentinels, I have difficulty imagining them being passed across TUs, so we could probably get away with a change. I would like a complete accounting of which types are changing from aggregate to non-aggregate. It may be safer to rename any such types with a 2 suffix, e.g. _Iotinel2, which could limit the blast radius.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like a complete accounting of which types are changing from aggregate to non-aggregate.

I checked this before and only found _Iotinel. Other iterator and sentinel types were non-aggregate initially.

I considered modifying _Iotinel as the following

struct _Iotinel {
    struct _Layer {
        /* [[no_unique_address]] */ _Bo _Last{};
    };
    // omitting other members
};

, which can keep _Iotinel an aggregate and avoid changing size and alignment (IIUC), while making is_constructible_v<_Iotinel, _Bo> false. Although this method still allows users to construct _Iotinel from _Bo using list-initialization.

Copilot AI review requested due to automatic review settings July 28, 2026 10:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

tests/std/tests/P3059R2_take_view_sentinel_constructor/test.compile.pass.cpp:50

  • Base is a simple view, so the non-const end() overload is disabled and both Sentinel and ConstSentinel name _Sentinel<true>. This assertion therefore checks ordinary copy construction, leaving the public <false>-to-<true> conversion and the private constructor on <false> untested. Use a non-simple base and first assert that the two aliases differ.
static_assert(constructible_from<ConstSentinel, Sentinel>);

tests/std/tests/P3059R2_take_while_view_sentinel_constructor/test.compile.pass.cpp:54

  • Because Base is a simple view, ranges::sentinel_t<View> uses the const end() overload; consequently Sentinel and ConstSentinel are both _Sentinel<true>. This only verifies copying and leaves the public <false>-to-<true> conversion and the private constructor on <false> untested. Use a non-simple base and first assert that the two sentinel aliases are distinct.
static_assert(constructible_from<ConstSentinel, Sentinel>);

tests/std/tests/P3059R2_join_view_sentinel_constructor/test.compile.pass.cpp:50

  • Base is a simple view, so non-const join_view::end() also returns _Sentinel<true> and this assertion tests its copy constructor rather than the <false>-to-<true> conversion. The private constructor on _Sentinel<false> is therefore uncovered. Use a non-simple outer range and assert that Sentinel and ConstSentinel differ.
static_assert(constructible_from<ConstSentinel, Sentinel>);

tests/std/tests/P3059R2_lazy_split_view_inner_outer_iterators_constructors/test.compile.pass.cpp:61

  • string_view and single_view are both simple views, so ForwardView::begin() selects _Outer_iter<true> even on a non-const view. Thus ForwardOuter equals ConstForwardOuter, this assertion only checks the copy constructor, and the forward-range _Outer_iter<false> constructor and conversion remain untested. Use a non-simple forward base and assert the aliases differ before checking conversion.
static_assert(constructible_from<ConstForwardOuter, ForwardOuter>);

tests/std/tests/P3059R2_elements_view_iterator_sentinel_constructors/test.compile.pass.cpp:77

  • Since Base is a simple view, the non-const elements_view overloads are disabled and Iterator/Sentinel already name the <true> specializations. Both assertions therefore test copy construction, leaving the <false> constructors and the intended non-const-to-const conversions uncovered. Use a non-simple base and assert that each non-const alias differs from its const counterpart.
static_assert(constructible_from<ConstIterator, Iterator>);
static_assert(constructible_from<ConstSentinel, Sentinel>);

@iskandarem

Copy link
Copy Markdown
Contributor Author

Some tests check if the views internal constructors of iterator/sentinel are accessible to users which this paper makes private.

Example:
tests\std\tests\P0896R4_views_filter_death\test.cpp:51

void test_constructor_wrong_range() {
    vector<int> vec0{0, 1, 2, 3};
    vector<int> vec1{4, 5, 6, 7};
    auto r0            = views::filter(vec0, lambda);
    using R            = decltype(r0);
    same_as<R> auto r1 = views::filter(vec1, lambda);
    ranges::iterator_t<R> i{r0, r1.begin().base()}; // vector iterators in range are from different containers
}

Should we remove these tests or should we just adjust them to right behavior?

@iskandarem
iskandarem marked this pull request as draft July 28, 2026 21:12
@StephanTLavavej

StephanTLavavej commented Jul 28, 2026

Copy link
Copy Markdown
Member

If such tests can't be exercised from user code (without granting friendship) then they should simply be removed, as the avenues for misuse have been closed off. (We should possibly consider removing same-parent checks from now-private constructors, or make them STL-internal checks.)

Copilot AI review requested due to automatic review settings July 29, 2026 14:49
@iskandarem
iskandarem marked this pull request as ready for review July 29, 2026 14:54
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (5)

tests/std/tests/P3059R2_take_view_sentinel_constructor/test.compile.pass.cpp:50

  • Base is a simple view, so the non-const take_view overloads are disabled and both Sentinel and ConstSentinel denote _Sentinel<true>. This assertion therefore checks ordinary copy construction, not the intended <false>-to-<true> conversion; the private constructor of _Sentinel<false> is also never tested. Please use a non-simple underlying view and assert that the two sentinel types differ before checking the conversion.
// The public conversion from the non-const sentinel specialization to the
// const sentinel specialization must remain available.
static_assert(constructible_from<ConstSentinel, Sentinel>);

tests/std/tests/P3059R2_take_while_view_sentinel_constructor/test.compile.pass.cpp:54

  • Base is a simple view, so take_while_view exposes _Sentinel<true> even through a non-const View; consequently Sentinel and ConstSentinel are the same type and this only tests the copy constructor. It also leaves the private constructor of _Sentinel<false> untested. Please use a non-simple underlying view and verify the types differ before asserting the conversion.
// The public converting constructor from sentinel<false> to sentinel<true>
// must remain available.
static_assert(constructible_from<ConstSentinel, Sentinel>);

tests/std/tests/P3059R2_join_view_sentinel_constructor/test.compile.pass.cpp:50

  • Because this subrange is a simple view, non-const join_view::end() already returns _Sentinel<true>. Thus Sentinel equals ConstSentinel, making this a copy-construction check and leaving both the <false> constructor privacy and the <false>-to-<true> conversion uncovered. Use a non-simple outer view and assert the sentinel types are distinct.
// The public conversion from sentinel<false> to sentinel<true> must remain
// available.
static_assert(constructible_from<ConstSentinel, Sentinel>);

tests/std/tests/P3059R2_lazy_split_view_inner_outer_iterators_constructors/test.compile.pass.cpp:61

  • ForwardBase and Pattern are both simple views, so ForwardView::begin() uses outer-iterator<true> even for a non-const view. ForwardOuter therefore equals ConstForwardOuter, and this assertion only exercises copying rather than the public <false>-to-<true> conversion it claims to test. Please use a non-simple forward base and assert the iterator types differ.
// The public conversion from outer-iterator<false> to outer-iterator<true>
// remains available.
static_assert(constructible_from<ConstForwardOuter, ForwardOuter>);

tests/std/tests/P3059R2_elements_view_iterator_sentinel_constructors/test.compile.pass.cpp:77

  • Base is a simple view, which disables the non-const elements_view overloads; Iterator/Sentinel therefore already denote the same <true> specializations as their Const aliases. These assertions only test copy construction, while the <false> implementation-only constructors and the intended <false>-to-<true> conversions remain uncovered. Use a non-simple base and first assert that each pair of types differs.
// The public non-const-to-const converting constructors must remain
// available.
static_assert(constructible_from<ConstIterator, Iterator>);
static_assert(constructible_from<ConstSentinel, Sentinel>);

@iskandarem
iskandarem marked this pull request as draft July 29, 2026 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cxx20 C++20 feature defect report Applied retroactively ranges C++20/23 ranges

Projects

Status: Work In Progress

Development

Successfully merging this pull request may close these issues.

P3059R2 Making User-Defined Constructors Of View Iterators/Sentinels private

4 participants