> You can write `operator!=(...) = default`, but no C++20 programmer ever would write it, because we have a rewrite rule that handles that transparently, without the programmer's intervention. So, it seems like it would make sense to handle postfix `++` in the same way, right?"
The question is why would anyone want to implement this operator ++(int)
[...]
So for example for implementing a view that would be a forward range,
the iterator must be a forward_iterator and have operator++(int) [...]
But when dealing with such complex iterators, a copy of an iterator
may cost a lot (like in my example).
So I usually declare them only and leave them as unresolved symbols,
that way I know code has optimal performance and this inefficient
operator is unused at all.
That seems like a good approach for your use-case, yeah. You could even declare-but-not-define the copy constructor itself!
With the rewrite-rule approach to `operator++`, we would have something like this:
// This is the only case where the proposed change has any effect
struct It1 {
It1& operator++() { ~~~~ }
~~~~
};
static_assert(std::forward_iterator<It1>);
int main() { ++it; it++; } // OK, it++ is implicitly rewritten to `[&](){ auto c = it; ++it; return c; }()`
// This is your use-case, unchanged by the proposal
struct It2 {
It2& operator++() { ~~~~ }
It2& operator++(int); // declared but not defined
~~~~
};
static_assert(std::forward_iterator<It2>);
int main() { ++it; it++; } // compiles, but does not link because it++ is never defined
// This is how you'd "opt out" of case #1, also happens to be legal today
struct It3 {
It3& operator++() { ~~~~ }
It3& operator++(int) = delete;
~~~~
};
static_assert(not std::forward_iterator<It3>); // because it++ is not well-formed
int main() { ++it; it++; } // does not compile because it++ is deleted
Maybe there is in future standards a way to opt out "operator ++(int)
= delete" and still have a valid forward iterator in view that meets
forard_range requirements ..
Well, every forward_iterator must be copyable. So it must be possible to do the-same-effect-as `it++`, manually:
auto copy = it;
++it;
use(copy);
And C++ gives us an actual syntax for that particular semantic: it's:
use(it++);
So any iterator that supports copying should also support `it++` — not to do so would be just perversely cumbersome for the iterator's user.
It's true that nobody should use `it++` in an algorithm that could just as well make do with `++it`. But that's nothing new. Nobody should use `--it` in an algorithm where they mean `++it`, either. We can't stop people from just literally writing their algorithm incorrectly, nor inefficiently. To the extent that the paper standard itself cares about efficiency, we express efficiency requirements via "Complexity" elements in the library clauses. A third-party library could do the same (e.g. say "This algorithm requires a forward iterator, but I promise I won't copy it any more than absolutely necessary"). A third-party library that never needs to copy the iterator should probably just document/constrain itself to accept input_iterator in the first place.
–Arthur