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?
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.