views:

129

answers:

2

I'm looking at the code for basic_string (that is bundled with g++ 4.2.1). The copy constructor makes use of a grab() function to "grab" a copy of a string (increment its reference-count):

_CharT* _M_grab( const _Alloc& __alloc1, const _Alloc& __alloc2 ) {
  return (!_M_is_leaked() && __alloc1 == __alloc2) ? _M_refcopy() : _M_clone(__alloc1);
}

This increments the reference-count only if the allocators for the two strings are the same -- makes sense. However, the copy constructor is:

basic_string(const basic_string& __str)
: _M_dataplus(__str._M_rep()->_M_grab(_Alloc(__str.get_allocator()), __str.get_allocator()),
              __str.get_allocator())
{ }

The first allocator that's passed to _M_grab() is a copy of the second. Why? The only way that operator==() for allocator might return false is if the user is using a custom allocator. However, even if that's true, you'd think that a copied allocator would compare equal to its original, right? So:

  1. Why compare allocators at all?
  2. Why copy-construct an allocator and compare the copy to the original?
  3. What's a use-case where comparing a copy with its original would return false?

Update

Yes, _M_grab() is used in one other place: for assignment. In this case, the allocators passed to _M_grab() are different. Fine. But there still seems to be no reason to both copy-construct and then compare allocators in the constructor for string.

+1  A: 

I know zip about the reasoning of the GCC team but here are my suppositions:

  1. For debugging? The allocators MUST be the same.

  2. So it can reuse _M_grab()?

  3. Should never happen?

anon
+1  A: 
  1. Allocators compare equal if objects allocated from one can be freed with the other. If that's the case, then two strings can share a reference to the same allocator; otherwise, each needs its own allocator.

  2. The comparison happens within _M_grab, which doesn't know that one argument has been copy-constructed from the other argument in this particular case. _M_grab is also called from assign, where the two strings may have different allocators.

  3. An allocator should always compare equal to a copy of itself.

Update

But there still seems to be no reason to both copy-construct and then compare allocators in the constructor for string.

Neither is there a particularly good reason to implement two almost-identical versions of _M_grab() to avoid the unnecessary comparison, which (for most allocators) will happen at compile time anyway. Maybe you think such a micro-optimisation would be desirable; apparently the author of this code didn't.

Mike Seymour
If #3 is always true, then why bother to copy construct it? Why not simply pass __str.get_allocator() for both arguments to _M_grab() ?
Paul J. Lucas
@Paul: You're right; it looks like the explicit copy is unnecessary, but harmless. `get_allocator()` returns a copy anyway.
Mike Seymour
My issue was more with the apparently unnecessary copy construction that with the comparison (which you addressed in your response to my comment); hence your later "Update" seems somewhat of a non sequitur.
Paul J. Lucas