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?

+4
source share
2 answers

, - 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>.

+1

, , .

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;
    }
}
0

Source: https://habr.com/ru/post/1680403/


All Articles