This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.

3169. ranges permutation generators discard useful information

Section: 27.8.13 [alg.permutation.generators] Status: C++20 Submitter: Casey Carter Opened: 2018-11-26 Last modified: 2021-02-25

Priority: 0

View all issues with C++20 status.

Discussion:

In the Ranges design, algorithms that necessarily traverse an entire range - consequently discovering the end iterator value - return that iterator value unless the algorithm's sole purpose is to return a derived value, for example, ranges::count. ranges::next_permutation and ranges::prev_permutation necessarily traverse the entirety of their range argument, but are currently specified to discard the end iterator value and return only a bool indicating whether they found a next (respectively previous) permutation or "reset" the range to the first (respectively last) permutation. They should instead return an aggregate composed of both that bool and the end iterator value to be consistent with the other range algorithms.

[2019-01-22; Daniel comments and updates wording]

During the reflector discussion it had been noticed that an additional update of 27.2 [algorithms.requirements] p.16 is necessary for the new type next_permutation_result and two missing occurrences of iterator_t<> where added. The proposed wording has been updated.

[2019-02-02 Priority to 0 and Status to Tentatively Ready after five positive votes on the reflector.]

Previous resolution [SUPERSEDED]:

  1. Modify 27.2 [algorithms.requirements] as follows:

    -16- The class templates binary_transform_result, for_each_result, minmax_result, mismatch_result, next_permutation_result, copy_result, and partition_copy_result have the template parameters, data members, and special members specified above. They have no base classes or members other than those specified.

  2. Modify 27.4 [algorithm.syn] as follows:

      // 27.8.13 [alg.permutation.generators], permutations
      template<class BidirectionalIterator>
        constexpr bool next_permutation(BidirectionalIterator first,
                                        BidirectionalIterator last);
      template<class BidirectionalIterator, class Compare>
        constexpr bool next_permutation(BidirectionalIterator first,
                                        BidirectionalIterator last, Compare comp);
    
      namespace ranges {
        template<class I>
        struct next_permutation_result {
          bool found;
          I in;
        };
    
        template<BidirectionalIterator I, Sentinel<I> S, class Comp = ranges::less<>,
                 class Proj = identity>
          requires Sortable<I, Comp, Proj>
          constexpr boolnext_permutation_result<I>
            next_permutation(I first, S last, Comp comp = {}, Proj proj = {});
        template<BidirectionalRange R, class Comp = ranges::less<>,
                 class Proj = identity>
          requires Sortable<iterator_t<R>, Comp, Proj>
          constexpr boolnext_permutation_result<iterator_t<R>>
            next_permutation(R&& r, Comp comp = {}, Proj proj = {});
      }
    
      template<class BidirectionalIterator>
        constexpr bool prev_permutation(BidirectionalIterator first,
                                        BidirectionalIterator last);
      template<class BidirectionalIterator, class Compare>
        constexpr bool prev_permutation(BidirectionalIterator first,
                                        BidirectionalIterator last, Compare comp);
    
      namespace ranges {
        template<class I>
        using prev_permutation_result = next_permutation_result<I>;
    
        template<BidirectionalIterator I, Sentinel<I> S, class Comp = ranges::less<>,
                 class Proj = identity>
          requires Sortable<I, Comp, Proj>
          constexpr boolprev_permutation_result<I>
            prev_permutation(I first, S last, Comp comp = {}, Proj proj = {});
        template<BidirectionalRange R, class Comp = ranges::less<>,
                 class Proj = identity>
          requires Sortable<iterator_t<R>, Comp, Proj>
          constexpr boolprev_permutation_result<iterator_t<R>>
            prev_permutation(R&& r, Comp comp = {}, Proj proj = {});
      }
    }
    
  3. Modify 27.8.13 [alg.permutation.generators] as follows:

    template<class BidirectionalIterator>
      constexpr bool next_permutation(BidirectionalIterator first,
                                      BidirectionalIterator last);
    template<class BidirectionalIterator, class Compare>
      constexpr bool next_permutation(BidirectionalIterator first,
                                      BidirectionalIterator last, Compare comp);
    
    namespace ranges {
      template<BidirectionalIterator I, Sentinel<I> S, class Comp = ranges::less<>,
               class Proj = identity>
        requires Sortable<I, Comp, Proj>
        constexpr boolnext_permutation_result<I>
          next_permutation(I first, S last, Comp comp = {}, Proj proj = {});
      template<BidirectionalRange R, class Comp = ranges::less<>,
               class Proj = identity>
        requires Sortable<iterator_t<R>, Comp, Proj>
        constexpr boolnext_permutation_result<iterator_t<R>>
          next_permutation(R&& r, Comp comp = {}, Proj proj = {});
    }
    
    […]

    -4- Returns: Let B be true if and only if a next permutation was found and otherwise false. Returns:

    • B for the overloads in namespace std, or

    • { B, last } for the overloads in namespace ranges.

    -5- Complexity: […]

    template<class BidirectionalIterator>
      constexpr bool prev_permutation(BidirectionalIterator first,
                                      BidirectionalIterator last);
    template<class BidirectionalIterator, class Compare>
      constexpr bool prev_permutation(BidirectionalIterator first,
                                      BidirectionalIterator last, Compare comp);
    
    namespace ranges {
      template<BidirectionalIterator I, Sentinel<I> S, class Comp = ranges::less<>,
               class Proj = identity>
        requires Sortable<I, Comp, Proj>
        constexpr boolprev_permutation_result<I>
          prev_permutation(I first, S last, Comp comp = {}, Proj proj = {});
      template<BidirectionalRange R, class Comp = ranges::less<>,
               class Proj = identity>
        requires Sortable<iterator_t<R>, Comp, Proj>
        constexpr boolprev_permutation_result<iterator_t<R>>
          prev_permutation(R&& r, Comp comp = {}, Proj proj = {});
    }
    
    […]

    -9- Returns: Let B be true if and only if a previous permutation was found and otherwise false. Returns:

    • B for the overloads in namespace std, or

    • { B, last } for the overloads in namespace ranges.

    -10- Complexity: […]

[2019-02-10 Tomasz comments; Casey updates the P/R and resets status to "Review."]

Shouldn't the range overloads for an algorithms return safe_iterator_t<R> instead of iterator_t<R>? Other algorithms are consistently returning the safe_iterator_t/safe_subrange_t in situation, when range argument is an rvalue (temporary) and returned iterator may be dangling.

[2019-02; Kona Wednesday night issue processing]

Status to Ready

Proposed resolution:

This wording is relative to N4800.

  1. Modify 27.2 [algorithms.requirements] as follows:

    -16- The class templates binary_transform_result, for_each_result, minmax_result, mismatch_result, next_permutation_result, copy_result, and partition_copy_result have the template parameters, data members, and special members specified above. They have no base classes or members other than those specified.

  2. Modify 27.4 [algorithm.syn] as follows:

      // 27.8.13 [alg.permutation.generators], permutations
      template<class BidirectionalIterator>
        constexpr bool next_permutation(BidirectionalIterator first,
                                        BidirectionalIterator last);
      template<class BidirectionalIterator, class Compare>
        constexpr bool next_permutation(BidirectionalIterator first,
                                        BidirectionalIterator last, Compare comp);
    
      namespace ranges {
        template<class I>
        struct next_permutation_result {
          bool found;
          I in;
        };
    
        template<BidirectionalIterator I, Sentinel<I> S, class Comp = ranges::less<>,
                 class Proj = identity>
          requires Sortable<I, Comp, Proj>
          constexpr boolnext_permutation_result<I>
            next_permutation(I first, S last, Comp comp = {}, Proj proj = {});
        template<BidirectionalRange R, class Comp = ranges::less<>,
                 class Proj = identity>
          requires Sortable<iterator_t<R>, Comp, Proj>
          constexpr boolnext_permutation_result<safe_iterator_t<R>>
            next_permutation(R&& r, Comp comp = {}, Proj proj = {});
      }
    
      template<class BidirectionalIterator>
        constexpr bool prev_permutation(BidirectionalIterator first,
                                        BidirectionalIterator last);
      template<class BidirectionalIterator, class Compare>
        constexpr bool prev_permutation(BidirectionalIterator first,
                                        BidirectionalIterator last, Compare comp);
    
      namespace ranges {
        template<class I>
        using prev_permutation_result = next_permutation_result<I>;
    
        template<BidirectionalIterator I, Sentinel<I> S, class Comp = ranges::less<>,
                 class Proj = identity>
          requires Sortable<I, Comp, Proj>
          constexpr boolprev_permutation_result<I>
            prev_permutation(I first, S last, Comp comp = {}, Proj proj = {});
        template<BidirectionalRange R, class Comp = ranges::less<>,
                 class Proj = identity>
          requires Sortable<iterator_t<R>, Comp, Proj>
          constexpr boolprev_permutation_result<safe_iterator_t<R>>
            prev_permutation(R&& r, Comp comp = {}, Proj proj = {});
      }
    }
    
  3. Modify 27.8.13 [alg.permutation.generators] as follows:

    template<class BidirectionalIterator>
      constexpr bool next_permutation(BidirectionalIterator first,
                                      BidirectionalIterator last);
    template<class BidirectionalIterator, class Compare>
      constexpr bool next_permutation(BidirectionalIterator first,
                                      BidirectionalIterator last, Compare comp);
    
    namespace ranges {
      template<BidirectionalIterator I, Sentinel<I> S, class Comp = ranges::less<>,
               class Proj = identity>
        requires Sortable<I, Comp, Proj>
        constexpr boolnext_permutation_result<I>
          next_permutation(I first, S last, Comp comp = {}, Proj proj = {});
      template<BidirectionalRange R, class Comp = ranges::less<>,
               class Proj = identity>
        requires Sortable<iterator_t<R>, Comp, Proj>
        constexpr boolnext_permutation_result<safe_iterator_t<R>>
          next_permutation(R&& r, Comp comp = {}, Proj proj = {});
    }
    
    […]

    -4- Returns: Let B be true if and only if a next permutation was found and otherwise false. Returns:

    • B for the overloads in namespace std, or

    • { B, last } for the overloads in namespace ranges.

    -5- Complexity: […]

    template<class BidirectionalIterator>
      constexpr bool prev_permutation(BidirectionalIterator first,
                                      BidirectionalIterator last);
    template<class BidirectionalIterator, class Compare>
      constexpr bool prev_permutation(BidirectionalIterator first,
                                      BidirectionalIterator last, Compare comp);
    
    namespace ranges {
      template<BidirectionalIterator I, Sentinel<I> S, class Comp = ranges::less<>,
               class Proj = identity>
        requires Sortable<I, Comp, Proj>
        constexpr boolprev_permutation_result<I>
          prev_permutation(I first, S last, Comp comp = {}, Proj proj = {});
      template<BidirectionalRange R, class Comp = ranges::less<>,
               class Proj = identity>
        requires Sortable<iterator_t<R>, Comp, Proj>
        constexpr boolprev_permutation_result<safe_iterator_t<R>>
          prev_permutation(R&& r, Comp comp = {}, Proj proj = {});
    }
    
    […]

    -9- Returns: Let B be true if and only if a previous permutation was found and otherwise false. Returns:

    • B for the overloads in namespace std, or

    • { B, last } for the overloads in namespace ranges.

    -10- Complexity: […]