Why does std::sub_match publicly inherit from s

2019-06-16 05:40发布

问题:

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

回答1:

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.



回答2:

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.



回答3:

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.



回答4:

Because they didn't need virtual destructor? ;-)



回答5:

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.