[libcxx-commits] [libcxx] 40aaa27 - [libc++][ranges] P2711R1 Making multi-param constructors of views explicit

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Thu Mar 23 10:30:28 PDT 2023


Author: Hristo Hristov
Date: 2023-03-23T19:30:21+02:00
New Revision: 40aaa272f145e633b29d5e70a4590cc425801f7e

URL: https://github.com/llvm/llvm-project/commit/40aaa272f145e633b29d5e70a4590cc425801f7e
DIFF: https://github.com/llvm/llvm-project/commit/40aaa272f145e633b29d5e70a4590cc425801f7e.diff

LOG: [libc++][ranges] P2711R1 Making multi-param constructors of views explicit

Implemented [[ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2711r1.html | P2711R1 ]] for existing views.
 (`join_with_view` is not yet implemented)

Reviewed By: #libc, philnik

Differential Revision: https://reviews.llvm.org/D144822

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx2b.rst
    libcxx/docs/Status/Cxx2bPapers.csv
    libcxx/include/__config
    libcxx/include/__ranges/drop_view.h
    libcxx/include/__ranges/drop_while_view.h
    libcxx/include/__ranges/filter_view.h
    libcxx/include/__ranges/iota_view.h
    libcxx/include/__ranges/lazy_split_view.h
    libcxx/include/__ranges/split_view.h
    libcxx/include/__ranges/take_view.h
    libcxx/include/__ranges/take_while_view.h
    libcxx/include/__ranges/transform_view.h
    libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop/ctor.view.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.drop/types.h
    libcxx/test/std/ranges/range.adaptors/range.filter/ctor.view_pred.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.split/ctor.range.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.split/ctor.view.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.take/ctor.view_count.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.take/types.h
    libcxx/test/std/ranges/range.adaptors/range.transform/ctor.view_function.pass.cpp
    libcxx/test/std/ranges/range.factories/range.iota.view/ctor.first.last.pass.cpp
    libcxx/test/std/ranges/range.factories/range.iota.view/ctor.value.bound.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx2b.rst b/libcxx/docs/Status/Cxx2b.rst
index 471b992fdc03b..3fbbb10f3e30b 100644
--- a/libcxx/docs/Status/Cxx2b.rst
+++ b/libcxx/docs/Status/Cxx2b.rst
@@ -43,6 +43,7 @@ Paper Status
    .. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but
       clang doesn't issue a diagnostic for deprecated using template declarations.
    .. [#note-P2520R0] P2520R0: Libc++ implemented this paper as a DR in C++20 as well.
+   .. [#note-P2711R1] P2711R1: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
 
 .. _issues-status-cxx2b:
 

diff  --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv
index e51ee27deb3c1..900130cfdd506 100644
--- a/libcxx/docs/Status/Cxx2bPapers.csv
+++ b/libcxx/docs/Status/Cxx2bPapers.csv
@@ -108,7 +108,7 @@
 "`P0290R4 <https://wg21.link/P0290R4>`__","LWG", "``apply()`` for ``synchronized_value<T>``","February 2023","","","|concurrency TS|"
 "`P2770R0 <https://wg21.link/P2770R0>`__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","","","|ranges|"
 "`P2164R9 <https://wg21.link/P2164R9>`__","LWG", "``views::enumerate``","February 2023","","","|ranges|"
-"`P2711R1 <https://wg21.link/P2711R1>`__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","","","|ranges|"
+"`P2711R1 <https://wg21.link/P2711R1>`__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","|Partial| [#note-P2711R1]_","","|ranges|"
 "`P2609R3 <https://wg21.link/P2609R3>`__","LWG", "Relaxing Ranges Just A Smidge","February 2023","","","|ranges|"
 "`P2713R1 <https://wg21.link/P2713R1>`__","LWG", "Escaping improvements in ``std::format``","February 2023","","","|format|"
 "`P2675R1 <https://wg21.link/P2675R1>`__","LWG", "``format``'s width estimation is too approximate and not forward compatible","February 2023","","","|format|"

diff  --git a/libcxx/include/__config b/libcxx/include/__config
index 3d3664eb83ae7..b9076073ab250 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -847,6 +847,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD
 #    define _LIBCPP_EXPLICIT_SINCE_CXX14 explicit
 #  endif
 
+#  if _LIBCPP_STD_VER >= 23
+#    define _LIBCPP_EXPLICIT_SINCE_CXX23 explicit
+#  else
+#    define _LIBCPP_EXPLICIT_SINCE_CXX23
+#  endif
+
 #  if _LIBCPP_STD_VER >= 14
 #    define _LIBCPP_CONSTEXPR_SINCE_CXX14 constexpr
 #  else

diff  --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h
index b97505b578552..87668c341cb67 100644
--- a/libcxx/include/__ranges/drop_view.h
+++ b/libcxx/include/__ranges/drop_view.h
@@ -74,7 +74,7 @@ namespace ranges {
     drop_view() requires default_initializable<_View> = default;
 
     _LIBCPP_HIDE_FROM_ABI
-    constexpr drop_view(_View __base, range_
diff erence_t<_View> __count)
+    constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 drop_view(_View __base, range_
diff erence_t<_View> __count)
       : __count_(__count)
       , __base_(std::move(__base))
     {

diff  --git a/libcxx/include/__ranges/drop_while_view.h b/libcxx/include/__ranges/drop_while_view.h
index 7c28992f18742..518feae4e2a98 100644
--- a/libcxx/include/__ranges/drop_while_view.h
+++ b/libcxx/include/__ranges/drop_while_view.h
@@ -51,7 +51,7 @@ class drop_while_view : public view_interface<drop_while_view<_View, _Pred>> {
     requires default_initializable<_View> && default_initializable<_Pred>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr drop_while_view(_View __base, _Pred __pred)
+  _LIBCPP_HIDE_FROM_ABI constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 drop_while_view(_View __base, _Pred __pred)
       : __base_(std::move(__base)), __pred_(std::in_place, std::move(__pred)) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&

diff  --git a/libcxx/include/__ranges/filter_view.h b/libcxx/include/__ranges/filter_view.h
index bf1481b7f9156..28d08c8a67e3b 100644
--- a/libcxx/include/__ranges/filter_view.h
+++ b/libcxx/include/__ranges/filter_view.h
@@ -64,10 +64,8 @@ namespace ranges {
     _LIBCPP_HIDE_FROM_ABI
     filter_view() requires default_initializable<_View> && default_initializable<_Pred> = default;
 
-    _LIBCPP_HIDE_FROM_ABI
-    constexpr filter_view(_View __base, _Pred __pred)
-      : __base_(std::move(__base)), __pred_(in_place, std::move(__pred))
-    { }
+    _LIBCPP_HIDE_FROM_ABI constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 filter_view(_View __base, _Pred __pred)
+        : __base_(std::move(__base)), __pred_(in_place, std::move(__pred)) {}
 
     template<class _Vp = _View>
     _LIBCPP_HIDE_FROM_ABI

diff  --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index 67cf0b73ecd65..57139426724bd 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -314,7 +314,7 @@ namespace ranges {
     constexpr explicit iota_view(_Start __value) : __value_(std::move(__value)) { }
 
     _LIBCPP_HIDE_FROM_ABI
-    constexpr iota_view(type_identity_t<_Start> __value, type_identity_t<_BoundSentinel> __bound_sentinel)
+    constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 iota_view(type_identity_t<_Start> __value, type_identity_t<_BoundSentinel> __bound_sentinel)
         : __value_(std::move(__value)), __bound_sentinel_(std::move(__bound_sentinel)) {
       // Validate the precondition if possible.
       if constexpr (totally_ordered_with<_Start, _BoundSentinel>) {
@@ -324,17 +324,17 @@ namespace ranges {
     }
 
     _LIBCPP_HIDE_FROM_ABI
-    constexpr iota_view(__iterator __first, __iterator __last)
+    constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 iota_view(__iterator __first, __iterator __last)
       requires same_as<_Start, _BoundSentinel>
     : iota_view(std::move(__first.__value_), std::move(__last.__value_)) {}
 
     _LIBCPP_HIDE_FROM_ABI
-    constexpr iota_view(__iterator __first, _BoundSentinel __last)
+    constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 iota_view(__iterator __first, _BoundSentinel __last)
       requires same_as<_BoundSentinel, unreachable_sentinel_t>
     : iota_view(std::move(__first.__value_), std::move(__last)) {}
 
     _LIBCPP_HIDE_FROM_ABI
-    constexpr iota_view(__iterator __first, __sentinel __last)
+    constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 iota_view(__iterator __first, __sentinel __last)
       requires(!same_as<_Start, _BoundSentinel> && !same_as<_Start, unreachable_sentinel_t>)
     : iota_view(std::move(__first.__value_), std::move(__last.__bound_sentinel_)) {}
 

diff  --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h
index b5b0e7ef02307..186a0af320f14 100644
--- a/libcxx/include/__ranges/lazy_split_view.h
+++ b/libcxx/include/__ranges/lazy_split_view.h
@@ -82,14 +82,14 @@ class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>>
     requires default_initializable<_View> && default_initializable<_Pattern> = default;
 
   _LIBCPP_HIDE_FROM_ABI
-  constexpr lazy_split_view(_View __base, _Pattern __pattern)
+  constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 lazy_split_view(_View __base, _Pattern __pattern)
     : __base_(std::move(__base)), __pattern_(std::move(__pattern)) {}
 
   template <input_range _Range>
     requires constructible_from<_View, views::all_t<_Range>> &&
              constructible_from<_Pattern, single_view<range_value_t<_Range>>>
   _LIBCPP_HIDE_FROM_ABI
-  constexpr lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
+  constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
     : __base_(views::all(std::forward<_Range>(__r)))
     , __pattern_(views::single(std::move(__e))) {}
 

diff  --git a/libcxx/include/__ranges/split_view.h b/libcxx/include/__ranges/split_view.h
index 6ebe5a43ed228..a27ac4ef7a196 100644
--- a/libcxx/include/__ranges/split_view.h
+++ b/libcxx/include/__ranges/split_view.h
@@ -75,13 +75,14 @@ class split_view : public view_interface<split_view<_View, _Pattern>> {
     requires default_initializable<_View> && default_initializable<_Pattern>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr split_view(_View __base, _Pattern __pattern)
+  _LIBCPP_HIDE_FROM_ABI constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 split_view(_View __base, _Pattern __pattern)
       : __base_(std::move(__base)), __pattern_(std::move((__pattern))) {}
 
   template <forward_range _Range>
     requires constructible_from<_View, views::all_t<_Range>> &&
                  constructible_from<_Pattern, single_view<range_value_t<_Range>>>
-  _LIBCPP_HIDE_FROM_ABI constexpr split_view(_Range&& __range, range_value_t<_Range> __elem)
+  _LIBCPP_HIDE_FROM_ABI constexpr _LIBCPP_EXPLICIT_SINCE_CXX23
+  split_view(_Range&& __range, range_value_t<_Range> __elem)
       : __base_(views::all(std::forward<_Range>(__range))), __pattern_(views::single(std::move(__elem))) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&

diff  --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index ec859e920ff17..111e7e5ba2516 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -67,7 +67,8 @@ class take_view : public view_interface<take_view<_View>> {
   _LIBCPP_HIDE_FROM_ABI
   take_view() requires default_initializable<_View> = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr take_view(_View __base, range_
diff erence_t<_View> __count)
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 take_view(_View __base, range_
diff erence_t<_View> __count)
       : __base_(std::move(__base)), __count_(__count) {
     _LIBCPP_ASSERT(__count >= 0, "count has to be greater than or equal to zero");
   }

diff  --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h
index 77d7390dceb9c..d1f1bfe75411f 100644
--- a/libcxx/include/__ranges/take_while_view.h
+++ b/libcxx/include/__ranges/take_while_view.h
@@ -67,7 +67,7 @@ class take_while_view : public view_interface<take_while_view<_View, _Pred>> {
     requires default_initializable<_View> && default_initializable<_Pred>
   = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr take_while_view(_View __base, _Pred __pred)
+  _LIBCPP_HIDE_FROM_ABI constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 take_while_view(_View __base, _Pred __pred)
       : __base_(std::move(__base)), __pred_(std::in_place, std::move(__pred)) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&

diff  --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index a71350f0c99dc..14bd400e6d079 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -71,7 +71,7 @@ class transform_view : public view_interface<transform_view<_View, _Fn>> {
     requires default_initializable<_View> && default_initializable<_Fn> = default;
 
   _LIBCPP_HIDE_FROM_ABI
-  constexpr transform_view(_View __base, _Fn __func)
+  constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 transform_view(_View __base, _Fn __func)
     : __func_(std::in_place, std::move(__func)), __base_(std::move(__base)) {}
 
   _LIBCPP_HIDE_FROM_ABI

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp
index cf9f9dbca9a56..326cabd637089 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop.while/ctor.view.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr drop_while_view(V base, Pred pred);
+// constexpr drop_while_view(V base, Pred pred); // explicit since C++23
 
 #include <cassert>
 #include <ranges>
@@ -16,6 +16,8 @@
 #include <utility>
 
 #include "MoveOnly.h"
+#include "test_convertible.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_base {
   MoveOnly mo;
@@ -32,9 +34,23 @@ struct Pred {
   bool operator()(int) const;
 };
 
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(!test_convertible<std::ranges::drop_while_view<View, Pred>, View, Pred>(),
+              "This constructor must be explicit");
+
+#else
+
+static_assert( test_convertible<std::ranges::drop_while_view<View, Pred>, View, Pred>(),
+              "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   {
-    std::ranges::drop_while_view<View, Pred> dwv = {View{{}, MoveOnly{5}}, Pred{}};
+    std::ranges::drop_while_view<View, Pred> dwv{View{{}, MoveOnly{5}}, Pred{}};
     assert(dwv.pred().moved);
     assert(!dwv.pred().copied);
     assert(std::move(dwv).base().mo.get() == 5);
@@ -45,5 +61,6 @@ constexpr bool test() {
 int main(int, char**) {
   test();
   static_assert(test());
+
   return 0;
 }

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/ctor.view.pass.cpp
index 504021aa9cc48..4f4257f9102cb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/ctor.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/ctor.view.pass.cpp
@@ -8,13 +8,28 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr drop_view(V base, range_
diff erence_t<V> count);
+// constexpr drop_view(V base, range_
diff erence_t<V> count);  // explicit since C++23
 
 #include <ranges>
 
+#include "test_convertible.h"
 #include "test_macros.h"
 #include "types.h"
 
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(!test_convertible<std::ranges::drop_view<View>, View, std::ranges::range_
diff erence_t<View>>(),
+              "This constructor must be explicit");
+
+#else
+
+static_assert(test_convertible<std::ranges::drop_view<View>, View, std::ranges::range_
diff erence_t<View>>(),
+              "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   std::ranges::drop_view dropView1(MoveOnlyView(), 4);
   assert(dropView1.size() == 4);

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
index b32c534a37e43..32bbddc05ed97 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h
@@ -94,4 +94,15 @@ struct CountedView : std::ranges::view_base {
   constexpr CountedIter end() const { return CountedIter(ForwardIter(globalBuff + 8)); }
 };
 
+struct View : std::ranges::view_base {
+  constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
+
+  constexpr int* begin() const { return begin_; }
+  constexpr int* end() const { return end_; }
+
+private:
+  int* begin_;
+  int* end_;
+};
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.filter/ctor.view_pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.filter/ctor.view_pred.pass.cpp
index 644f8deba6e85..3ccab93397147 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.filter/ctor.view_pred.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.filter/ctor.view_pred.pass.cpp
@@ -8,12 +8,14 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr filter_view(View, Pred);
-
-#include <ranges>
+// constexpr filter_view(View, Pred); // explicit since C++23
 
 #include <cassert>
+#include <ranges>
 #include <utility>
+
+#include "test_convertible.h"
+#include "test_macros.h"
 #include "types.h"
 
 struct Range : std::ranges::view_base {
@@ -41,6 +43,20 @@ struct TrackingRange : TrackInitialization, std::ranges::view_base {
   int* end() const;
 };
 
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(!test_convertible<std::ranges::filter_view<Range, Pred>, Range, Pred>(),
+              "This constructor must be explicit");
+
+#else
+
+static_assert( test_convertible<std::ranges::filter_view<Range, Pred>, Range, Pred>(),
+              "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
 
@@ -57,19 +73,6 @@ constexpr bool test() {
     assert(it == end);
   }
 
-  // Test implicit syntax
-  {
-    Range range(buff, buff + 8);
-    Pred pred;
-    std::ranges::filter_view<Range, Pred> view = {range, pred};
-    auto it = view.begin(), end = view.end();
-    assert(*it++ == 1);
-    assert(*it++ == 3);
-    assert(*it++ == 5);
-    assert(*it++ == 7);
-    assert(it == end);
-  }
-
   // Make sure we move the view
   {
     bool moved = false, copied = false;

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp
index ebf1356afff60..91df304b79af7 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp
@@ -11,7 +11,7 @@
 // template <input_range Range>
 //   requires constructible_from<View, views::all_t<Range>> &&
 //             constructible_from<Pattern, single_view<range_value_t<Range>>>
-// constexpr lazy_split_view(Range&& r, range_value_t<Range> e);
+// constexpr lazy_split_view(Range&& r, range_value_t<Range> e); // explicit since C++23
 
 #include <ranges>
 
@@ -20,6 +20,8 @@
 #include <string_view>
 #include <type_traits>
 #include <utility>
+
+#include "test_convertible.h"
 #include "types.h"
 
 struct ElementWithCounting {
@@ -88,6 +90,22 @@ static_assert( std::ranges::random_access_range<StrView>);
 static_assert( std::ranges::view<StrView>);
 static_assert( std::is_copy_constructible_v<StrView>);
 
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(
+    !test_convertible<std::ranges::lazy_split_view<StrView, StrView>, StrView, std::ranges::range_value_t<StrView>>(),
+    "This constructor must be explicit");
+
+#else
+
+static_assert(
+    test_convertible<std::ranges::lazy_split_view<StrView, StrView>, StrView, std::ranges::range_value_t<StrView>>(),
+    "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   {
     using V = std::ranges::lazy_split_view<StrView, StrView>;

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp
index 264e883beeaea..e7bf052a7e9ee 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp
@@ -8,13 +8,14 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr lazy_split_view(View base, Pattern pattern);
-
-#include <ranges>
+// constexpr lazy_split_view(View base, Pattern pattern); // explicit since C++23
 
 #include <cassert>
+#include <ranges>
 #include <string_view>
 #include <utility>
+
+#include "test_convertible.h"
 #include "types.h"
 
 struct ViewWithCounting : std::ranges::view_base {
@@ -41,9 +42,27 @@ struct ViewWithCounting : std::ranges::view_base {
   constexpr ViewWithCounting& operator=(ViewWithCounting&&) = default;
   constexpr bool operator==(const ViewWithCounting&) const { return true; }
 };
+
 static_assert(std::ranges::forward_range<ViewWithCounting>);
 static_assert(std::ranges::view<ViewWithCounting>);
 
+using View = ViewWithCounting;
+using Pattern = ViewWithCounting;
+
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(!test_convertible<std::ranges::lazy_split_view<View, Pattern>, View, Pattern>(),
+              "This constructor must be explicit");
+
+#else
+
+static_assert( test_convertible<std::ranges::lazy_split_view<View, Pattern>, View, Pattern>(),
+              "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   // Calling the constructor with `(ForwardView, ForwardView)`.
   {
@@ -62,9 +81,6 @@ constexpr bool test() {
 
   // Make sure the arguments are moved, not copied.
   {
-    using View = ViewWithCounting;
-    using Pattern = ViewWithCounting;
-
     // Arguments are lvalues.
     {
       int view_copied = 0, view_moved = 0, pattern_copied = 0, pattern_moved = 0;

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.split/ctor.range.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/ctor.range.pass.cpp
index 605e3d544b2d8..bbe08befdb419 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/ctor.range.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/ctor.range.pass.cpp
@@ -11,7 +11,7 @@
 // template <input_range Range>
 //   requires constructible_from<View, views::all_t<Range>> &&
 //             constructible_from<Pattern, single_view<range_value_t<Range>>>
-// constexpr split_view(Range&& r, range_value_t<Range> e);
+// constexpr split_view(Range&& r, range_value_t<Range> e); // explicit since C++23
 
 #include <algorithm>
 #include <cassert>
@@ -21,6 +21,9 @@
 #include <type_traits>
 #include <utility>
 
+#include "test_convertible.h"
+#include "test_macros.h"
+
 struct Counting {
   int* times_copied = nullptr;
   int* times_moved  = nullptr;
@@ -68,6 +71,22 @@ static_assert(std::ranges::random_access_range<StrView>);
 static_assert(std::ranges::view<StrView>);
 static_assert(std::is_copy_constructible_v<StrView>);
 
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(
+    !test_convertible<std::ranges::split_view<StrView, StrView>, StrView, std::ranges::range_value_t<StrView>>(),
+    "This constructor must be explicit");
+
+# else
+
+static_assert(
+    test_convertible<std::ranges::split_view<StrView, StrView>, StrView, std::ranges::range_value_t<StrView>>(),
+    "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   {
     using V = std::ranges::split_view<StrView, StrView>;

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.split/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/ctor.view.pass.cpp
index ad206ee5ed751..963f85f8c478f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/ctor.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/ctor.view.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr split_view(View base, Pattern pattern);
+// constexpr split_view(View base, Pattern pattern); // explicit since C++23
 
 #include <algorithm>
 #include <cassert>
@@ -16,6 +16,9 @@
 #include <string_view>
 #include <utility>
 
+#include "test_convertible.h"
+#include "test_macros.h"
+
 struct ViewWithCounting : std::ranges::view_base {
   int* times_copied = nullptr;
   int* times_moved  = nullptr;
@@ -38,6 +41,23 @@ struct ViewWithCounting : std::ranges::view_base {
   constexpr bool operator==(const ViewWithCounting&) const { return true; }
 };
 
+using View    = ViewWithCounting;
+using Pattern = ViewWithCounting;
+
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(!test_convertible<std::ranges::split_view<View, Pattern>, View, Pattern>(),
+              "This constructor must be explicit");
+
+#else
+
+static_assert( test_convertible<std::ranges::split_view<View, Pattern>, View, Pattern>(),
+              "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   {
     std::string_view input = "abc def";
@@ -48,9 +68,6 @@ constexpr bool test() {
 
   // Make sure the arguments are moved, not copied.
   {
-    using View    = ViewWithCounting;
-    using Pattern = ViewWithCounting;
-
     // Arguments are lvalues.
     {
       int view_copied = 0, view_moved = 0, pattern_copied = 0, pattern_moved = 0;

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp
index 7adeb6713680a..469b2698c8844 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr take_while_view(V base, Pred pred);
+// constexpr take_while_view(V base, Pred pred); // explicit since C++23
 
 #include <cassert>
 #include <ranges>
@@ -16,6 +16,8 @@
 #include <utility>
 
 #include "MoveOnly.h"
+#include "test_convertible.h"
+#include "test_macros.h"
 
 struct View : std::ranges::view_base {
   MoveOnly mo;
@@ -32,9 +34,23 @@ struct Pred {
   bool operator()(int) const;
 };
 
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(!test_convertible<std::ranges::take_while_view<View, Pred>, View, Pred>(),
+              "This constructor must be explicit");
+
+#else
+
+static_assert(test_convertible<std::ranges::take_while_view<View, Pred>, View, Pred>(),
+              "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   {
-    std::ranges::take_while_view<View, Pred> twv = {View{{}, MoveOnly{5}}, Pred{}};
+    std::ranges::take_while_view<View, Pred> twv{View{{}, MoveOnly{5}}, Pred{}};
     assert(twv.pred().moved);
     assert(!twv.pred().copied);
     assert(std::move(twv).base().mo.get() == 5);
@@ -45,5 +61,6 @@ constexpr bool test() {
 int main(int, char**) {
   test();
   static_assert(test());
+
   return 0;
 }

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.take/ctor.view_count.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/ctor.view_count.pass.cpp
index 63b936da98181..f37ffb0825ac1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/ctor.view_count.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/ctor.view_count.pass.cpp
@@ -8,16 +8,31 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr take_view(V base, range_
diff erence_t<V> count);
+// constexpr take_view(V base, range_
diff erence_t<V> count); // explicit since C++23
 
-#include <ranges>
 #include <cassert>
+#include <ranges>
 
-#include "test_macros.h"
+#include "test_convertible.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 #include "test_range.h"
 #include "types.h"
 
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(!test_convertible<std::ranges::take_view<View>, View, std::ranges::range_
diff erence_t<View>>(),
+              "This constructor must be explicit");
+
+#else
+
+static_assert(test_convertible<std::ranges::take_view<View>, View, std::ranges::range_
diff erence_t<View>>(),
+              "This constructor must be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
 

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
index 09549a9e086f0..db80e68bb21af 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h
@@ -54,4 +54,15 @@ static_assert(std::ranges::view<SizedRandomAccessView>);
 static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
 static_assert(std::ranges::sized_range<SizedRandomAccessView>);
 
+struct View : std::ranges::view_base {
+  constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { }
+
+  constexpr int* begin() const { return begin_; }
+  constexpr int* end() const { return end_; }
+
+private:
+  int* begin_;
+  int* end_;
+};
+
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.view_function.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.view_function.pass.cpp
index 7ce042603694d..63a43d189256f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.view_function.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.view_function.pass.cpp
@@ -8,14 +8,16 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr transform_view(View, F);
+// constexpr transform_view(View, F); // explicit since C++23
 
+#include <cassert>
 #include <ranges>
 
-#include <cassert>
+#include "test_convertible.h"
+#include "test_macros.h"
 
 struct Range : std::ranges::view_base {
-  constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) { }
+  constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
   constexpr int* begin() const { return begin_; }
   constexpr int* end() const { return end_; }
 
@@ -28,6 +30,20 @@ struct F {
   constexpr int operator()(int i) const { return i + 100; }
 };
 
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(!test_convertible<std::ranges::transform_view<Range, F>, Range, F>(),
+              "This constructor must be explicit");
+
+#else
+
+static_assert( test_convertible<std::ranges::transform_view<Range, F>, Range, F>(),
+              "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
 
@@ -41,16 +57,6 @@ constexpr bool test() {
     assert(view[7] == 108);
   }
 
-  {
-    Range range(buff, buff + 8);
-    F f;
-    std::ranges::transform_view<Range, F> view = {range, f};
-    assert(view[0] == 101);
-    assert(view[1] == 102);
-    // ...
-    assert(view[7] == 108);
-  }
-
   return true;
 }
 

diff  --git a/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.first.last.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.first.last.pass.cpp
index 0b02cadc32609..ee0e7fceffa61 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.first.last.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.first.last.pass.cpp
@@ -8,14 +8,55 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
-// constexpr iota_view(iterator first, see below last);
+// constexpr iota_view(iterator first, see below last); // explicit since C++23
 
 #include <ranges>
 #include <cassert>
 
+#include "test_convertible.h"
 #include "test_macros.h"
 #include "types.h"
 
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+std::ranges::iota_view<SomeInt, SomeInt> view;
+
+static_assert(!test_convertible<std::ranges::iota_view<SomeInt, SomeInt>,
+                                decltype(std::ranges::iota_view<SomeInt, SomeInt>{}.begin()),
+                                decltype(std::ranges::iota_view<SomeInt, SomeInt>{}.end())>(),
+              "This constructor must be explicit");
+
+static_assert(!test_convertible<std::ranges::iota_view<SomeInt>,
+                                decltype(std::ranges::iota_view{SomeInt{0}}.begin()),
+                                decltype(std::unreachable_sentinel)>(),
+              "This constructor must be explicit");
+
+static_assert(!test_convertible<std::ranges::iota_view<SomeInt, IntComparableWith<SomeInt>>,
+                                decltype(std::ranges::iota_view{SomeInt(0), IntComparableWith(SomeInt(10))}.begin()),
+                                decltype(std::ranges::iota_view{SomeInt(0), IntComparableWith(SomeInt(10))}.end())>(),
+              "This constructor must be explicit");
+
+#else
+
+static_assert(test_convertible<std::ranges::iota_view<SomeInt, SomeInt>,
+                               decltype(std::ranges::iota_view<SomeInt, SomeInt>{}.begin()),
+                               decltype(std::ranges::iota_view<SomeInt, SomeInt>{}.end())>(),
+              "This constructor must not be explicit");
+
+static_assert(test_convertible<std::ranges::iota_view<SomeInt>,
+                               decltype(std::ranges::iota_view{SomeInt{0}}.begin()),
+                               decltype(std::unreachable_sentinel)>(),
+              "This constructor must not be explicit");
+
+static_assert(test_convertible<std::ranges::iota_view<SomeInt, IntComparableWith<SomeInt>>,
+                               decltype(std::ranges::iota_view{SomeInt(0), IntComparableWith(SomeInt(10))}.begin()),
+                               decltype(std::ranges::iota_view{SomeInt(0), IntComparableWith(SomeInt(10))}.end())>(),
+              "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   {
     std::ranges::iota_view commonView(SomeInt(0), SomeInt(10));

diff  --git a/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.value.bound.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.value.bound.pass.cpp
index 906e0e092d2a6..7528e1ccf3ee0 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.value.bound.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/ctor.value.bound.pass.cpp
@@ -14,13 +14,52 @@ TEST_CLANG_DIAGNOSTIC_IGNORED("-Wsign-compare")
 TEST_GCC_DIAGNOSTIC_IGNORED("-Wsign-compare")
 TEST_MSVC_DIAGNOSTIC_IGNORED(4018 4389) // various "signed/unsigned mismatch"
 
-// constexpr iota_view(type_identity_t<W> value, type_identity_t<Bound> bound);
+// constexpr iota_view(type_identity_t<W> value, type_identity_t<Bound> bound); // explicit since C++23
 
 #include <ranges>
 #include <cassert>
 
+#include "test_convertible.h"
 #include "types.h"
 
+// SFINAE tests.
+
+#if TEST_STD_VER >= 23
+
+static_assert(!test_convertible<std::ranges::iota_view<SomeInt, SomeInt>,
+                                decltype(std::ranges::iota_view<SomeInt, SomeInt>{}.begin()),
+                                decltype(std::ranges::iota_view<SomeInt, SomeInt>{}.end())>(),
+              "This constructor must be explicit");
+
+static_assert(!test_convertible<std::ranges::iota_view<SomeInt>,
+                                decltype(std::ranges::iota_view<SomeInt>{}.begin()),
+                                decltype(std::unreachable_sentinel)>(),
+              "This constructor must be explicit");
+
+static_assert(!test_convertible<std::ranges::iota_view<SomeInt, IntComparableWith<SomeInt>>,
+                                decltype(std::ranges::iota_view{SomeInt(0), IntComparableWith(SomeInt(10))}.begin()),
+                                decltype(std::ranges::iota_view{SomeInt(0), IntComparableWith(SomeInt(10))}.end())>(),
+              "This constructor must be explicit");
+
+#else
+
+static_assert( test_convertible<std::ranges::iota_view<SomeInt, SomeInt>,
+                                decltype(std::ranges::iota_view<SomeInt, SomeInt>{}.begin()),
+                                decltype(std::ranges::iota_view<SomeInt, SomeInt>{}.end())>(),
+              "This constructor must not be explicit");
+
+static_assert( test_convertible<std::ranges::iota_view<SomeInt>,
+                                decltype(std::ranges::iota_view<SomeInt>{}.begin()),
+                                decltype(std::unreachable_sentinel)>(),
+              "This constructor must not be explicit");
+
+static_assert( test_convertible<std::ranges::iota_view<SomeInt, IntComparableWith<SomeInt>>,
+                                decltype(std::ranges::iota_view{SomeInt(0), IntComparableWith(SomeInt(10))}.begin()),
+                                decltype(std::ranges::iota_view{SomeInt(0), IntComparableWith(SomeInt(10))}.end())>(),
+              "This constructor must not be explicit");
+
+#endif // TEST_STD_VER >= 23
+
 constexpr bool test() {
   {
     std::ranges::iota_view<SomeInt, SomeInt> io(SomeInt(0), SomeInt(10));


        


More information about the libcxx-commits mailing list