Can std :: shared_ptr <std :: string const> serve as an efficient implementation of immutable strings with reference counting?
Ideally, an immutable string class will require only one memory allocation for each row. Even a reference counter can be stored in the same part of memory that contains the actual string.
A trivial implementation stringand shared_ptrhave identified three separate parts of memory shared_ptr<string const>:
- Memory for string buffer
- Memory for string object
- Reference counter memory
Now I know that when used, an std::make_shared()intelligent implementation can combine the last two in one distribution. But that still leaves two distributions.
When you know that a string is immutable, the string buffer will not be redistributed, so it can be integrated with a string object, leaving only one selection.
I know that some string implementations already use such optimizations for short strings, but I am implementing an implementation that does this regardless of the length of the string.
My questions: Is my judicious sound? Is implementation allowed and capable? Can I reasonably expect from a standard standard library to implement this optimization? Do you know about modern library implementations that do this?
Or is that something I would have to implement myself?
, - make_shared, . , ++ 17 ( shared_ptr ).
Boost, , boost::make_shared, . , ; shared_ptr<char[]>, , ( , std::string.
boost, . , .
- , O (1), , ( std::string_view s). . ( , .)
, std::unordered_set<std::string>.
, , .
class ImmutableStringAllocator;
template<typename CharT>
using immutable_string = std::basic_string<CharT, std::char_traits<CharT>, ImmutableStringAllocator>
template<size_t N>
immutable_string<char> make_immutable_string(char (&data)[N])
{
ImmutableStringAllocator alloc(N);
// going for basic_string::basic_string(charT *, size_t, Allocator)
return allocate_shared<immutable_string<char>>(alloc, data, N, alloc);
}
class ImmutableStringAllocator {
size_t len;
size_t offset;
char * buf;
std::reference_wrapper<char *> ref;
public:
// Normal Allocator stuff here
ImmutableStringAllocator(size_t N) : len(N), buf(nullptr), offset(0), ref(buf) {}
ImmutableStringAllocator(const ImmutableStringAllocator & other) : len(other.len), buf(nullptr), offset(other.offset), ref(other.buf) {}
ImmutableStringAllocator operator=(const ImmutableStringAllocator & other)
{
assert(buf == nullptr);
temp(other);
swap(*this, temp);
return *this;
}
pointer allocate(size_type n, const_void_pointer hint)
{
if (!ref.get()) { buf = ::new(n + len); offset = n; return buf; }
return ref.get() + offset;
}
}