From 62b66f4ec42d4cb1670bd97ad4d5085e3b9c5500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Kr=C3=BCgler?= Date: Sat, 14 Oct 2023 18:19:24 +0200 Subject: [PATCH] New issue from Hewill Kang: "projected should just be I" --- xml/issue3996.xml | 99 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 xml/issue3996.xml diff --git a/xml/issue3996.xml b/xml/issue3996.xml new file mode 100644 index 0000000000..9fb492c612 --- /dev/null +++ b/xml/issue3996.xml @@ -0,0 +1,99 @@ + + + + +<tt>projected<I, identity></tt> should just be <tt>I</tt> +
+Hewill Kang +12 Oct 2023 +99 + + +

+Currently, projected is a wrapper of the implementation type regardless of whether Proj is identity. +

+Since identity always returns a reference, this prevents projected<I, identity> from fully +emulating the properties of the original iterator when its reference is a prvalue. +

+Such non-equivalence may lead to unexpected behavior in some cases (demo): +

+
+#include <algorithm>
+#include <ranges>
+#include <iostream>
+
+int main() {
+  auto outer = std::views::iota(0, 5)
+             | std::views::transform([](int i) {
+                 return std::views::single(i) | std::views::filter([](int) { return true; });
+               });
+  
+  for (auto&& inner : outer)
+    for (auto&& elem : inner)
+      std::cout << elem << " "; // 0 1 2 3 4 
+  
+  std::ranges::for_each(
+    outer,
+    [](auto&& inner) {
+      // error: passing 'const filter_view' as 'this' argument discards qualifiers
+      for (auto&& elem : inner)
+        std::cout << elem << " ";
+    });
+}
+
+

+In the above example, ranges::for_each requires indirect_unary_predicate<Pred, projected<I, identity>> +which ultimately requires invocable<Pred&, iter_common_reference_t<projected<I, identity>>>. +

+According to the current wording, the reference and value type of projected<I, identity> are filter_view&& +and filter_view& respectively, which causes its common reference to be eventually calculated as +const filter_view&. Since the former is not const-iterable, this results in a hard error during +instantiation because const begin is called unexpectedly in an unconstrained lambda. +

+

+It seems like having projected<I, identity> just be I is a more appropriate choice, +which makes the concept checking really specific to I rather than a potentially incomplete iterator wrapper. +

+
+ + +

+This wording is relative to . +

+ +
    + +
  1. Modify as indicated:

    + +
    +

    +-1- Class template projected is used to constrain algorithms that accept callable objects and projections +(). It combines an indirectly_readable type I and a callable +object type Proj into a new indirectly_readable type whose reference type is the +result of applying Proj to the iter_reference_t of I. +

    +
    +namespace std {
    +  template<class I, class Proj>
    +  struct projected-impl {                               // exposition only
    +    struct type {                                       // exposition only
    +      using value_type = remove_cvref_t<indirect_result_t<Proj&, I>>;
    +      using difference_type = iter_difference_t<I>;     // present only if I
    +                                                        // models weakly_incrementable
    +      indirect_result_t<Proj&, I> operator*() const;    // not defined
    +    };
    +  };
    +
    +  template<indirectly_readable I, indirectly_regular_unary_invocable<I> Proj>
    +    using projected = conditional_t<is_same_v<Proj, identity>, I, typename projected-impl<I, Proj>::type>;
    +}
    +
    +
    + + +
  2. + +
+
+ +