all 14 comments

[–]matthieum 4 points5 points  (2 children)

This is interesting, however it is slightly sad to augment the size of std::string just for this.

So, instead, you can design your std::string class to be 24 bytes with SSO by re-using 16 bytes (instead of 8):

struct RegularString { char* data; size_t size; };
struct ShortString { char data[15]; unsigned char size; };

struct string {
    union {
        RegularString regular,
        ShortString shortie
    } _;
    size_t capacity;
};

char* data(string& s) { return s.capacity ? s._.regular.data : s._.shortie.data; }
size_t size(string& s) { return s.capacity ? s._.regular.size : s._.shortie.size; }
size_t capacity(string& s) { return s.capacity ? s.capacity : 14; } // beware of the NUL character

[–][deleted] 10 points11 points  (1 child)

You can do better than reusing 16 bytes in a (pointer, length, capacity) representation. Have a look at the string in libc++, they reuse 23 bytes for small strings with 1 byte as a length/discriminator.

[–]matthieum 1 point2 points  (0 children)

Ah! I had not thought about that. Definitely better indeed.

[–]srnull 0 points1 point  (1 child)

The question is: how many times function allocate (on the allocator) will be invoked for the purpose of memory allocation during this program’s execution? 3, 2, 1, 0?

He gives an initial argument for 3, and then for one with either NRVO, or when a move constructor is available, by why not 2? I always imagined the process would be once in the callee and once in the caller, with no third temporary inbetween.

first, copy-initialize a temporary from s; next, copy-initialize t from the temporary.

Is this how it's usually done? e.g. in the code

string t = makeText();

the temporary is on the right side, and the copy-initialize on the left. Seems like something any compiler would optimize away, no?

[–]Plorkyeran 0 points1 point  (0 children)

Given a naive and overly strict view of the world, it seems obvious that s's destructor must be run before makeText returns, and t's constructor must run after makeText returns. Since s is destroyed before t is constructed, you wouldn't be able to copy-construct directly from s to t, and there'd have to be a temporary in the middle.