I was reading the documentation of std::sub_match<BidirectionalIterator>
and saw that it publicly inherits from std::pair<BidirectionalIterator, BidirectionalIterator>
. Since a sub_match
is simply a pair of iterators into a sequence of characters, with some additional functions, I can understand that it is implemented with a pair
, but why use public inheritance?
The problem with inheriting publicly from std::pair<T,U>
is the same as inheriting publicly from most other standard classes: they are not meant to be manipulated polymorphically (notably they do not define a virtual destructor). Other members will also fail to work properly, namely the assignment operator and the swap member function (they will not copy the matched
member of sub_match
).
Why did Boost developers and then the committee decided to implement sub_match
by inheriting publicly from pair
instead of using composition (or private inheritance with using declarations if they wanted to keep member access through first
and second
)?
It's an interesting question. Presumably, they considered it safe
because no one would ever dynamically allocate one anyway. About the
only way you're going to get sub_match
objects is as a return value
from some of the functions of basic_regex
, or as copies of other
sub_match
, and all of these will be either temporaries or local
variables.
Note that it's not safe to keep sub_match
objects around anyway, since
they contain iterators whose lifetime... doesn't seem to be specified in
the standard. Until the match_results
object is reused? Until the
string
operand to the function which filled in the match_results
object is destructed? Or?
I'd still have avoided the public inheritence. But in this case, it's
not as dangerous as it looks, because there's really no reason you'd
ever want to dynamically allocate a sub_match
.
Because C++ has no way to inherit an interface without public inheritance. You can inherit an implementation with private inheritance, but then everything is private. If you want the same interface as a std::pair
, you have to be a std::pair
.
Also, consider this. This is obviously undefined behavior:
std::sub_match<BidirectionalIterator> theMatch = ...;
std::pair<BidirectionalIterator> *pMatch = &theMatch;
delete pMatch;
But so is this:
std::sub_match<BidirectionalIterator> theMatch = ...;
std::pair<BidirectionalIterator> *pMatch = &theMatch.pair;
delete pMatch;
Why is the first one so much more of a concern than the second?
sub_match
and pair
are light-weight objects (depending on their contents of course). They are meant to be copied or passed by reference, all of which is 100% safe. There is little if any reason to allocate them on the heap and use them through pointers. So while I understand your concern, I think it's unlikely to happen in any real code.
Here's what regex
's author has to say about it: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1429.htm#matches_discussion
Nothing very specific to your question, I'm afraid.
I would guess that this decision was a trade off between reinventing the wheel and introducing a little risk of misuse. Note that in general there is no need to construct a sub_match
, they are returned from regex
function. Moreover pairs of iterators are a very practical way of implementing ranges.
Because they didn't need virtual destructor? ;-)
If std::sub_match<BidirectionalIterator>
has no state of its own, then it is fine for it to inherit from std::pair
. Don't do it at home though.