P3785R1
Library Wording Changes for Defaulted Postfix Increment and Decrement Operations

Published Proposal,

Authors:
Audience:
LWG
Project:
ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21

Abstract

Following design-approval of P3668 Defaulting Postfix Increment and Decrement Operations, it would be possible to significantly shrink the standard library wording by defaulting those operations where appropriate. This paper proposes this as a wording-only change for LWG review.

1. Revision History

Changes from R1:

2. Motivation and Scope

[P3668] proposes explicitly defaulting postfix increment and decrement operations, giving them a definition equivalent to:
C operator++(C& c, int){
    C copy = C(c);
    ++c;
    return copy;
}

This allows users to write shorter code using this sensible default with a universally-accepted meaning. The standard library also defines many objects with postfix increment and decrement operations, and each one is defined in full with code which is equivalent to the above definition. For example, std::regex_iterator defines its postfix increment operation as follows:

regex_iterator operator++(int);
9 Effects: As if by:

regex_iterator tmp = *this;
++(*this);
return tmp;

This pattern of explicitly defining the default meaning of the postfix operation is repeated on every iterator which exhibits the default behaviour in the standard. While this has been necessary up to this point, with P3668 core-approved for C++29, we can allow for a more concise library specification. We are able to mark these operations as defaulted, which allows us to keep their meanings clear while eliminating many unnecessary boilerplate definitions from the standard document.

We consider this a strictly non-semantic, wording only change to the specification; and do not anticipate that implementations will have to update around this paper. The semantics of every affected overload after this proposal are identical to before it; however the specification for them is permitted to be shorter and simpler.

3. Proposal

The authors have identified 51 candidate operations in the standard which may be defaulted. Most of these are simple: they are a single postfix increment or decrement operator which has behaviour equivalent to the defaulted definition which can be immediately replaced. There are also some which we highlight for specific consideration as they have additional properties which LWG may prefer not to word as defaulted. The authors are not library wording experts, so defer such decisions to LWG.

3.1. Simple Defaultable Operations

The following table lists all standard library postfix operations which are specified as exactly equivalent to the canonical definition. This expressly does not include any iterators which have any additional complexities which might raise wording questions if they were instead specified as = default. Those are covered in the next section. The below tables feature the a column "noexcept status", where ✓ means that the implicitly-added noexcept status from defaulting a function is consistent with the dependent operations (ie, either all suboperations are marked noexcept and the defaulted function is marked noexcept, or at least one suboperation is not marked noexcept and the defaulted function is also not marked noexcept), and ✗ means that defaulting the postfix operator will result in a breaking or inconsistent noexcept specification (see § 3.4 LWG Feedback and Direction).

Standard Library Operation Specified in Specified as noexcept status
move_iterator::operator--(int) [move.iter.nav] As if by:
move_iterator tmp = *this;
--current;
return tmp;
std::basic_const_iterator::operator++(int) [const.iterators.ops] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::basic_const_iterator::operator--(int) [const.iterators.ops] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::counted_iterator::operator--(int) [counted.iterator.nav] Equivalent to:
counted_iterator tmp = *this;
--*this;
return tmp;
std::regex_iterator::operator++(int) [re.regiter.iter] As if by:
regex_iterator tmp = *this;
++(*this);
return tmp;
std::regex_token_iterator::operator++(int) [re.tokiter.incr] Effects: Constructs a copy tmp of *this, then calls ++(*this).
Returns: tmp.
std::ranges::iota_view::operator++(int) [range.iota.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::iota_view::operator--(int) [range.iota.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::ranges::repeat_view::iterator::operator++(int) [range.repeat.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::filter_view::iterator::operator++(int) [range.filter.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::filter_view::iterator::operator--(int) [range.filter.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::ranges::transform_view::iterator::operator++(int) [range.transform.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::transform_view::iterator::operator--(int) [range.transform.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::ranges::chunk_view::iterator::operator--(int) for forward ranges [range.chunk.fwd.iter] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::ranges::join_view::iterator::operator++(int) [range.join.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::join_view::iterator::operator--(int) [range.join.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::ranges::join_with_view::iterator::operator++(int) [range.join.with.iterator] Equivalent to:
iterator tmp = *this;
++*this;
return tmp;
std::ranges::join_with_view::iterator::operator--(int) [range.join.with.iterator] Equivalent to:
iterator tmp = *this;
--*this;
return tmp;
std::ranges::split_view::iterator::operator++(int) [range.split.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::elements_view::iterator::operator++(int) [range.elements.iterator] Equivalent to:
auto temp = *this;
++current_;
return temp;

NB: operator++() is specified as:

++current_;
return *this;
std::ranges::elements_view::iterator::operator--(int) [range.elements.iterator] Equivalent to:
auto temp = *this;
--current_;
return temp;

NB: operator--() is specified as:

--current_;
return *this;
std::ranges::enumerate_view::iterator::operator++(int) [range.enumerate.iterator] Equivalent to:
auto temp = *this;
++*this;
return temp;
std::ranges::enumerate_view::iterator::operator--(int) [range.enumerate.iterator] Equivalent to:
auto temp = *this;
--*this;
return temp;
std::ranges::zip_view::iterator::operator++(int) [range.zip.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::zip_view::iterator::operator--(int) [range.zip.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::ranges::zip_transform_view::iterator::operator++(int) [range.zip.transform.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::zip_transform_view::iterator::operator--(int) [range.zip.transform.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::ranges::adjacent_transform_view::iterator::operator++(int) [range.adjacent.transform.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::adjacent_transform_view::iterator::operator--(int) [range.adjacent.transform.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::ranges::chunk_by_view::iterator::operator--(int) [range.chunk.by.iter] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::ranges::stride_view::iterator::operator++(int) [range.stride.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::stride_view::iterator::operator--(int) [range.stride.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
std::ranges::cartesian_product_view::iterator::operator++(int) [range.cartesian.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
std::ranges::cartesian_product_view::iterator::operator--(int) [range.cartesian.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;

3.2. Postfix Operations Requiring LWG Input

This table lists postfix operations which have equivalent semantics to the default behaviour, but which have complexities in the specification, and so may require some input from library wording experts. We have intentionally made this table as conservative as possible and intentionally list things which we believe will be uncontentious to change; but explicitly want to ensure that any wording questions, no matter how trivial, are noted. LWG feedback was that all of these categories are permissible to default under the correct circumstances; however we retain this table for easy categorisation of design intent. For the sake of easier formatting, the "Reason" column will also list noexcept consistency as described above.

To define the terms we use to describe why we believe an operator belongs in this table:

Standard Library Operation Specified in Specified as Reason and noexcept status
std::istream_iterator::operator++(int) [istream.iterator.ops] Preconditions: in_stream != nullptr is true.
Effects: Equivalent to:
istream_iterator tmp = *this;
++*this;
return tmp;
Precondition
noexcept: ✓
move_iterator::operator++(int) [move.iter.nav] Effects: If Iterator models forward_iterator, equivalent to:
move_iterator tmp = *this;
++current;
return tmp;
Otherwise, equivalent to ++current.
Compile-time Branching
noexcept: ✓
std::ranges::lazy_split_view::outer_iterator::operator++(int) [range.lazy.split.outer]
 if constexpr (forward_range<Base>) {
    auto tmp = *this;
    ++*this;
    return tmp;
} else
    ++*this;
}
Compile-time Branching
noexcept: ✓
std::ranges::lazy_split_view::inner_iterator::operator++(int) [range.lazy.split.inner]
 if constexpr (forward_range<Base>) {
    auto tmp = *this;
    ++*this;
    return tmp;
} else
    ++*this;
}
Compile-time Branching
noexcept: ✓
std::common_iterator::operator++(int) [common.iter.nav] Preconditions: holds_alternative<I>(v_) is true.
Effects: If I models forward_iterator, equivalent to:
common_iterator tmp = *this;
++*this;
return tmp;
Otherwise: ...
Precondition and Compile-time branching
noexcept: ✓
std::ranges::repeat_view::iterator::operator--(int) [range.repeat.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
Transitive precondition on operator--()
noexcept: ✓
std::counted_iterator::operator++(int) [counted.iterator.nav] Equivalent to:
counted_iterator tmp = *this;
++*this;
return tmp;
Transitive precondition on operator++()
noexcept: ✓
std::ranges::concat_view::iterator::operator++(int) [range.concat.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
Transitive precondition on operator++()
noexcept: ✓
std::ranges::concat_view::iterator::operator--(int) [range.concat.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
Transitive precondition on operator--()
noexcept: ✓
std::ranges::adjacent_view::iterator::operator++(int) [range.adjacent.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
Transitive precondition on operator++()
noexcept: ✓
std::ranges::adjacent_view::iterator::operator--(int) [range.adjacent.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
Transitive precondition on operator--()
noexcept: ✓
std::ranges::chunk_view::iterator::operator++(int) for forward ranges [range.chunk.fwd.iter] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
Transitive precondition on operator++()
noexcept: ✓
std::ranges::slide_view::iterator::operator++(int) [range.slide.iterator] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
Transitive precondition on operator++()
noexcept: ✓
std::ranges::slide_view::iterator::operator--(int) [range.slide.iterator] Equivalent to:
auto tmp = *this;
--*this;
return tmp;
Transitive precondition on operator--()
noexcept: ✓
std::ranges::chunk_by_view::iterator::operator++(int) [range.chunk.by.iter] Equivalent to:
auto tmp = *this;
++*this;
return tmp;
Transitive precondition on operator++()
noexcept: ✓

To save some counting, we did not find any postfix operations which would receive a breaking noexcept specification.

3.3. Container Iterators and Named Requirements

The iterators for standard library containers are not specified in terms of specific functions in the same way the above operations are, but instead as being some type which meets one or more named requirements or iterator concepts. For example, std::vector::iterator must meet Cpp17RandomAccessIterator and model std::contiguous_iterator; and the standard defers to these rather than providing a dedicated section for std::vector::iterator::operator++(int).

We do not propose changing the contents of the named requirements tables or the wording for the standard iterator concepts. The purpose of this proposal is a net simplification of standard library wording and we feel that this would work against that goal. The most we would suggest is adding a note to the table to state that a defaulted operation is valid for that case, but we defer to LWG’s judgement on whether it is necessary.

3.4. LWG Feedback and Direction

LWG reviewed this paper at the Brno meeting, on 2026-06-10. Guidance on how best to shape the wording was given. In particular the three categories of possible contention listed in the previous section were resolved as follows:

Several other points were raised. In particular, as a defaulted function will implicitly inherit the noexcept specification of its suboperations, we need to ensure that this does not result in a breaking change in the wording by adding a defaulted overload. Implementations are welcome to strengthen the noexcept specification as they see fit. However, we do not believe that the implicit addition of noexcept will cause a problem, as most specified prefix operations and copy operations are not marked noexcept. For the sake of completeness we do note the noexcept status in every example in the tables in § 3 Proposal.

It was also raised that we should drop unnecessary constexpr on newly-defaulted postfix increment and decrement operators, as all defaulted functions are implicitly constexpr. Similarly, advice was given that in the case where a postfix increment or decrement operator function has the same constraints as its prefix equivalent, we can remove the constraints and preconditions from the postfix operation as these will be de facto inherited from the checks on the precondition function.

LWG also advised that we drop any changes to std::simd types, as this was better deferred to be handled alongside specific std::simd library papers.

4. Proposed Wording

Modify [const.iterators.iterator] as follows:

constexpr basic_const_iterator& operator++();
constexpr void operator++(int);
constexpr basic_const_iterator operator++(int) requires forward_iterator<Iterator> = default;

constexpr basic_const_iterator& operator--() requires bidirectional_iterator<Iterator>;
constexpr basic_const_iterator operator--(int) requires bidirectional_iterator<Iterator> = default;

Modify [const.iterators.ops] as follows:

constexpr basic_const_iterator operator++(int) requires forward_iterator<Iterator>;
10Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr basic_const_iterator operator--(int) requires bidirectional_iterator<Iterator>;
12Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [move.iterator] as follows:

constexpr move_iterator& operator++();
constexpr autovoid operator++(int);
move_iterator operator++(int) requires forward_iterator<Iterator> = default;
constexpr move_iterator& operator--();
constexpr move_iterator operator--(int) = default;

Modify [move.iter.nav] as follows:

constexpr autovoid operator++(int);
3 Effects: If Iterator models forward_iterator, equivalent to:

move_iterator tmp = *this;
++current;
return tmp;

Otherwise, equivalent to ++current.

3 Effects: Equivalent to ++current.

[...]

constexpr move_iterator operator--(int);
6Effects: As if by:

move_iterator tmp = *this;
--current;
return tmp;

Modify [common.iterator] as follows:

constexpr common_iterator& operator++();
constexpr decltype(auto) operator++(int);
common_iterator operator++(int) requires forward_iterator<I> = default;

Modify [common.iter.nav] as follows:

constexpr common_iterator& operator++()
1Hardened Preconditions: holds_alternative<I>(v_) is true.
2Effects: Equivalent to ++get<I>(v_).
3Returns: *this.

constexpr decltype(auto) operator++(int);
4Hardened Preconditions: holds_alternative<I>(v_) is true.
5Effects: If I models forward_iterator, equivalent to:

common_iterator tmp = *this;
++*this;
return tmp;

Otherwise, if If requires(I& i) { { *i++ } -> can-reference; } is true or

indirectly_readable<I> && constructible_from<iter_value_t<I>, iter_reference_t<I>> &&
move_constructible<iter_value_t<I>>

is false, equivalent to:

return get<I>(v_)++;

Otherwise, equivalent to:

postfix-proxy p(**this);
++*this;
return p;

where postfix-proxy is the exposition-only class:

class postfix-proxy {
    iter_value_t<I> keep_;
    constexpr postfix-proxy(iter_reference_t<I>&& x)
        : keep_(std::forward<iter_reference_t<I>>(x)) {}
    public:
    constexpr const iter_value_t<I>& operator*() const noexcept {
        return keep_;
    }
};

Modify [counted.iterator] as follows:

constexpr counted_iterator& operator++();
constexpr decltype(auto) operator++(int);
constexpr counted_iterator operator++(int)
    requires forward_iterator<I>  = default;
constexpr counted_iterator& operator--() 
    requires bidirectional_iterator<I>;
constexpr counted_iterator operator--(int) 
    requires bidirectional_iterator<I> = default;

Modify [counted.iterator.nav] as follows:

constexpr counted_iterator operator++(int) requires forward_iterator<I>
5Effects: Equivalent to:

counted_iterator tmp = *this;
++*this;
return tmp;

[...]

constexpr counted_iterator operator--(int)
requires bidirectional_iterator<I>

7Effects: Equivalent to:

counted_iterator tmp = *this;
--*this;
return tmp;

Modify [istream.iterator.general] as follows:

istream_iterator& operator++();
istream_iterator  operator++(int) = default;

Modify [istream.iterator.ops] as follows:

istream_iterator& operator++();
5Preconditions: in_stream != nullptr is true.
6Effects: Equivalent to:
if(!(*in_stream >> value))
    in_stream = nullptr;
7Returns: *this

istream_iterator operator++(int);
8Preconditions: in_stream != nullptr is true.
9 Effects: Equivalent to:

istream_iterator tmp = *this;
++*this;
return tmp;

Modify [range.iota.iterator] as follows:

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires incrementable<W> = default;

constexpr iterator& operator--() requires decrementable<W>;
constexpr iterator operator--(int) requires decrementable<W> = default;

[...]

constexpr iterator operator++(int) requires incrementable<W>;
8 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires decrementable<W>;
10 Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.repeat.iterator] as follows:

constexpr iterator& operator++();
constexpr iterator operator++(int) = default;

constexpr iterator& operator--();
constexpr iterator operator--(int) = default;

[...]

constexpr iterator operator++(int);
6 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int);
9Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.filter.iterator] as follows:

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires forward_range<Base> = default;

constexpr iterator& operator--() requires bidirectional_range<Base>;
constexpr iterator operator--(int) requires bidirectional_range<Base> = default;

[...]

constexpr iterator operator++(int) requires forward_range<Base>;
12 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<Base>;
14 Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.transform.iterator] as follows:

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires forward_range<Base> = default;

constexpr iterator& operator--() requires bidirectional_range<Base>;
constexpr iterator operator--(int) requires bidirectional_range<Base> = default;

[...]

constexpr iterator operator++(int) requires forward_range<Base>;
9 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<Base>;
11 Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.chunk.fwd.iter] as follows:

constexpr value_type operator*() const;
constexpr iterator& operator++();
constexpr iterator operator++(int) = default;

constexpr iterator& operator--() requires bidirectional_range<Base>;
constexpr iterator operator--(int) requires bidirectional_range<Base> = default;

[...]

constexpr iterator operator++(int);
9Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<Base>;
11Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.join.iterator] as follows:

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int)
    requires ref-is-glvalue && forward_range<Base> &&
            forward_range<range_reference_t<Base>> = default;

constexpr iterator& operator--()
    requires ref-is-glvalue && bidirectional_range<Base> &&
            bidirectional_range<range_reference_t<Base>> &&
            common_range<range_reference_t<Base>>;

constexpr iterator operator--(int)
    requires ref-is-glvalue && bidirectional_range<Base> &&
            bidirectional_range<range_reference_t<Base>> &&
            common_range<range_reference_t<Base>> = default;

[...]

constexpr iterator operator++(int) requires ref-is-glvalue && forward_range<Base> && forward_range<range_reference_t<Base>>;
15 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> && bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>;
17 Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.join.with.iterator] as follows:

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int)
    requires ref-is-glvalue && forward_iterator<OuterIter> &&
            forward_iterator<InnerIter> = default;

constexpr iterator& operator--()
    requires ref-is-glvalue && bidirectional_range<Base> &&
            bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
constexpr iterator operator--(int)
    requires ref-is-glvalue && bidirectional_range<Base> &&
            bidirectional-common<InnerBase> && bidirectional-common<PatternBase> = default;

[...]

constexpr iterator operator++(int) requires ref-is-glvalue && forward_iterator<OuterIter> && forward_iterator<InnerIter>;
15 Effects: Equivalent to:

iterator tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> && bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
17 Effects: Equivalent to:

iterator tmp = *this;
--*this;
return tmp;

Modify [range.lazy.split.outer] as follows:

constexpr outer-iterator& operator++();

constexpr decltype(auto) operator++(int) {
    if constexpr (forward_range<Base>) {
        auto tmp = *this;
        ++*this;
        return tmp;
    } else
        ++*this;
}


constexpr void operator++(int) {
    ++*this;
}
outer-iterator operator++(int) requires forward_range<Base> = default;


friend constexpr bool operator==(const outer-iterator& x, const outer-iterator& y)
    requires forward_range<Base>;

Modify [range.lazy.split.inner] as follows:

constexpr inner-iterator& operator++();

constexpr decltype(auto) operator++(int) {
    if constexpr (forward_range<Base>) {
        auto tmp = *this;
        ++*this;
        return tmp;
    } else
        ++*this;
}


constexpr void operator++(int) {
    ++*this;
}
inner-iterator operator++(int) requires forward_range<Base> = default;


friend constexpr bool operator==(const inner-iterator& x, const inner-iterator& y)
    requires forward_range<Base>;

Modify [range.split.iterator] as follows:

constexpr iterator& operator++();
constexpr iterator operator++(int) = default;

[...]

constexpr iterator operator++(int);
5 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

Modify [range.concat.iterator] as follows:

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int)
    requires all-forward<Const, Views...> = default;
constexpr iterator& operator--()
    requires concat-is-bidirectional<Const, Views...>;
constexpr iterator operator--(int)
    requires concat-is-bidirectional<Const, Views...> = default;

[...]

constexpr iterator operator++(int) requires all-forward<Const, Views...>;
15Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires concat-is-bidirectional<Const, Views...>;
18Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.elements.iterator] as follows:

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires forward_range<Base> = default;

constexpr iterator& operator--() requires bidirectional_range<Base>;
constexpr iterator operator--(int) requires bidirectional_range<Base> = default;

[...]

constexpr iterator operator++(int) requires forward_range<Base>;
10 Effects: Equivalent to:

auto temp = *this;
++current_;
return temp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<Base>;
12 Effects: Equivalent to:

auto temp = *this;
--current_;
return temp;

Modify [range.enumerate.iterator] as follows:

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires forward_range<Base> = default;

constexpr iterator& operator--() requires bidirectional_range<Base>;
constexpr iterator operator--(int) requires bidirectional_range<Base> = default;

[...]

constexpr iterator operator++(int) requires forward_range<Base>;
9 Effects: Equivalent to:

auto temp = *this;
++*this;
return temp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<Base>;
11 Effects: Equivalent to:

auto temp = *this;
--*this;
return temp;

Modify [range.zip.iterator] as follows:

constexpr auto operator*() const;
constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires all-forward<Const, Views...> = default;

constexpr iterator& operator--() requires all-bidirectional<Const, Views...>;
constexpr iterator operator--(int) requires all-bidirectional<Const, Views...> = default;

[...]

constexpr iterator operator++(int) requires all-forward<Const, Views...>;
9 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires all-bidirectional<Const, Views...>;
11 Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.zip.transform.iterator] as follows:

constexpr decltype(auto) operator*() const noexcept(see below);
constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires forward_range<Base> = default;

constexpr iterator& operator--() requires bidirectional_range<Base>;
constexpr iterator operator--(int) requires bidirectional_range<Base> = default;

[...]

constexpr iterator operator++(int) requires forward_range<Base>;
8 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<Base>;
10 Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.adjacent.iterator] as follows:

constexpr iterator& operator++();
constexpr iterator operator++(int) = default;

constexpr iterator& operator--() requires bidirectional_range<Base>;
constexpr iterator operator--(int) requires bidirectional_range<Base> = default;

[...]

constexpr iterator operator++(int);
10Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<Base>;
14Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.adjacent.transform.iterator] as follows:

constexpr decltype(auto) operator*() const noexcept(see below);
constexpr iterator& operator++();
constexpr iterator operator++(int) = default;
constexpr iterator& operator--() requires bidirectional_range<Base>;
constexpr iterator operator--(int) requires bidirectional_range<Base> = default;
constexpr iterator& operator+=(difference_type x) requires random_access_range<Base>;
constexpr iterator& operator-=(difference_type x) requires random_access_range<Base>;

[...]

constexpr iterator operator++(int);
7 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<Base>;
9 Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.slide.iterator] as follows:

constexpr auto operator*() const;
constexpr iterator& operator++();
constexpr iterator operator++(int) = default;

constexpr iterator& operator--() requires bidirectional_range<Base>;
constexpr iterator operator--(int) requires bidirectional_range<Base> = default;

[...]

constexpr iterator operator++(int);
10Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<Base>;
14Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.chunk.by.iter] as follows:

constexpr value_type operator*() const;
constexpr iterator& operator++();
constexpr iterator operator++(int) = default;

constexpr iterator& operator--() requires bidirectional_range<V>;
constexpr iterator operator--(int) requires bidirectional_range<V> = default;

[...]

constexpr iterator operator++(int);
7Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<V>;
9 Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.stride.iterator] as follows:

constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires forward_range<Base> = default;

constexpr iterator& operator--() requires bidirectional_range<Base>;
constexpr iterator operator--(int) requires bidirectional_range<Base> = default;

[...]

constexpr iterator operator++(int) requires forward_range<Base>;
10 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires bidirectional_range<Base>;
12 Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [range.cartesian.iterator] as follows:

constexpr auto operator*() const;
constexpr iterator& operator++();
constexpr void operator++(int);
constexpr iterator operator++(int) requires forward_range<maybe-const<Const, First>> = default;

constexpr iterator& operator--()
    requires cartesian-product-is-bidirectional<Const, First, Vs...>;
constexpr iterator operator--(int)
    requires cartesian-product-is-bidirectional<Const, First, Vs...> = default;

[...]

constexpr iterator operator++(int) requires forward_range<maybe-const<Const, First>>;
14 Effects: Equivalent to:

auto tmp = *this;
++*this;
return tmp;

[...]

constexpr iterator operator--(int) requires cartesian-product-is-bidirectional<Const, First, Vs...>;
16 Effects: Equivalent to:

auto tmp = *this;
--*this;
return tmp;

Modify [re.regiter.general] as follows:

regex_iterator& operator++();
regex_iterator operator++(int) = default;

Modify [re.regiter.incr] as follows:

regex_iterator operator++(int);
9 Effects: As if by:

regex_iterator tmp = *this;
++(*this);
return tmp;

Modify [re.tokiter.general] as follows:

regex_token_iterator& operator++();
regex_token_iterator operator++(int) = default;

Modify [re.tokiter.incr] as follows:

regex_token_iterator& operator++(int);
9Effects: Constructs a copy tmp of *this, then calls ++(*this)
10Returns: tmp

References

Non-Normative References

[P3668]
Matthew Taylor, Alex. Defaulting Postfix Increment and Decrement Operations. URL: https://isocpp.org/files/papers/P3668R4.html