8

Why does the new std::sentinel_for concept require that the sentinel type is default_initializable (via semiregular)? Doesn't that rule out a large class of useful sentinel types where default construction doesn't make any sense?

Example:

//Iterate over a string until given character or '\0' is found
class char_sentinel
{
public:
    char_sentinel(char end) :
        end_character(end)
    { }

   friend bool operator==(const char* lhs, char_sentinel rhs)
   {
       return (*lhs == '\0') || (*lhs == rhs.end_character);
   }

   friend bool operator!=(const char* lhs, char_sentinel rhs) { ... }
   friend bool operator==(char_sentinel lhs, const char* rhs) { ... }
   friend bool operator!=(char_sentinel lhs, const char* rhs) { ... }

private:
    char end_character; 
};

I know I can add a default constructor which initializes to '\0' but what if I consider that as misuse of the structure and want to discourage that?

bolov
  • 72,283
  • 15
  • 145
  • 224
Mestkon
  • 3,532
  • 7
  • 18
  • The explanation in the duplicate link only moves the question to be "Why isn't all iterators required to be default constructible?". Why doesn't input iterators need to be default constructible as well? – Mestkon Jul 16 '20 at 22:11
  • We've got someone asking on the cpplang Slack too, and I don't know the answer. The linked question about `ostream_iterator` (which is an iterator not a sentinel, and isn't a sentinel for itself precisely _because_ [it isn't default-constructible](https://en.cppreference.com/w/cpp/iterator/ostream_iterator/ostream_iterator)) isn't a duplicate of this question at all. – Quuxplusone Feb 03 '22 at 22:38
  • @Quuxplusone Well more importantly it's also not a sentinel because it doesn't have (and never had, unlike the default constructor) equality. But the question "why do output iterators need to be default constructible" is really quite similar to "why do sentinels need to be default constructible," and has the same answer (except that we removed the former restriction). – Barry Feb 04 '22 at 04:22

1 Answers1

3

While this isn't a strict duplicate of this question, my answer there largely still applies.

The Elements of Programming design philosophy of how types behave is that they should be regular, which EoP defined as:

T’s computational basis includes equality, assignment, destructor, default constructor, copy constructor, total ordering (or default total ordering) and underlying type

I think over time this definition has proven to not be the most useful one. Equality isn't necessary for a lot of algorithms, hence semiregular. But even default construction isn't necessary in many contexts.

While we removed the default construction requirement from input iterators, output iterators, and views in P2325R3, that paper did not touch (or even discuss) sentinels. The argument for sentinels would largely be the same - that it's not an important requirement, and algorithms don't need it either - but we just didn't pursue weakening sentinel_for from requiring semiregular to just requiring copyable. I'm not sure we had a strong reason to avoid doing so, other than just not being worth it, since sentinels are written less often than iterators anyway.

Barry
  • 286,269
  • 29
  • 621
  • 977