On Wed, 2016-08-10 at 21:31 -0400, Trevor Saunders wrote: > > Well, I'd say the compromises made in std::string make it pretty > terrible for general purpose use accept where perf and memory doesn't > matter at all. > > std::vector isn't very great size wise either, imho the size / > capacity fields would be much better put in the buffer than in the > struct itself, to save 2 * sizeof (void *) in sizeof std::vector.
There's nothing in the standard that says those fields have to go into the vector struct/class itself and not into the data buffer. It just happens to be the way it's implemented in libstdc++. E.g. libc++ implements some things in different ways to save a few bytes here and there. It looks more practical to put those fields into the container itself, otherwise you'd have to do a nullptr check every time you access the size field etc. Although newer compilers optimize away the redundant nullptr checks, older compilers (which GCC wants to be good with) might not do it. In any case, it seems to generate slightly bigger code, at least in one instance (see attachment). Anyway, normally there is more data in the vectors than there are vectors around, so whether sizeof (vector) is 8 or 24 bytes doesn't matter. > > or just having your stack_string type convert to the normal string > type so that you can pass mutable references. But that would imply copying, wouldn't it? Cheers, Oleg
#if 0 template <typename T> class vector { public: unsigned int size (void) const { return m_buffer != nullptr ? m_buffer->size : 0; } bool empty (void) const { return size () == 0; } unsigned int capacity (void) const { return m_buffer != nullptr ? m_buffer->capacity : 0; } T* data (void) { return (T*)(m_buffer + 1); } const T* data (void) const { return (const T*)(m_buffer + 1); } T& operator [] (unsigned int i) { return data ()[i]; } const T& operator [] (unsigned int i) const { return data ()[i]; } private: struct buffer_header { unsigned int size; unsigned int capacity; }; buffer_header* m_buffer; }; int foo (const vector<int>& x) { if (x.empty ()) return 0; int r = 0; for (unsigned int i = 0; i < x.size (); ++i) r += x[i]; return r; } /* -O2 -m2a mov.l @r4,r2 tst r2,r2 bt .L5 mov.l @r2,r1 tst r1,r1 bt.s .L5 shll2 r1 add #-4,r1 shlr2 r1 add #8,r2 mov #0,r0 add #1,r1 .align 2 .L3: mov.l @r2+,r3 dt r1 bf.s .L3 add r3,r0 rts/n .align 1 .L5: rts mov #0,r0 */ #endif #if 1 template <typename T> class vector { public: unsigned int size (void) const { return m_size; } bool empty (void) const { return size () == 0; } unsigned int capacity (void) const { return m_capacity; } T* data (void) { return (T*)(m_buffer); } const T* data (void) const { return (const T*)(m_buffer); } T& operator [] (unsigned int i) { return data ()[i]; } const T& operator [] (unsigned int i) const { return data ()[i]; } private: unsigned int m_size; unsigned int m_capacity; T* m_buffer; }; int foo (const vector<int>& x) { if (x.empty ()) return 0; int r = 0; for (unsigned int i = 0; i < x.size (); ++i) r += x[i]; return r; } /* -O2 -m2a mov.l @r4,r1 tst r1,r1 bt.s .L7 mov #0,r0 shll2 r1 mov.l @(8,r4),r2 add #-4,r1 shlr2 r1 add #1,r1 .align 2 .L3: mov.l @r2+,r3 dt r1 bf.s .L3 add r3,r0 .L7: rts/n */ #endif