I have a very complex string class whose implementation uses copy-on-write, so it has a special proxy class arec_t to determine whether access to a member is a write or a read, defined roughly like this
class arec_t: non_copyable,non_moveable{
string_t* _to;
size_t _index;
friend class string_t;
public:
arec_t(string_t* to,size_t index)noexcept:_to(to),_index(index){}
arec_t(special_init_t,const arec_t&ref)noexcept:_to(ref._to),_index(ref._index){}
[[nodiscard]]operator char_T()&&noexcept{ return _to->arec(_index); }
[[nodiscard]]operator char_T()const&&noexcept{ return _to->arec(_index); }
arec_t&& operator=(char_T a)&&noexcept{
_to->arec_set(_index,a);
return move(*this);
}
arec_t&& operator=(const arec_t&&ch)&&noexcept{ return move(*this).operator=(move(ch).operator char_T()); }
private:
[[nodiscard]]char_T* get_address()noexcept{ return _to->unique_c_str()+_index; }
[[nodiscard]]const char_T* get_address()const noexcept{ return (add_const(_to))->c_str()+_index; }
public:
[[nodiscard]]char_T* operator&()&&noexcept{ return get_address(); }
[[nodiscard]]const char_T* operator&()const&&noexcept{ return get_address(); }
[[nodiscard]]explicit operator char_T&()&&noexcept{ return *get_address(); }
};
[[nodiscard]]arec_t operator[](size_t index)noexcept{ return{this,index}; }
[[nodiscard]]const arec_t operator[](size_t index)const noexcept{ return{remove_const(this),index}; }
Most of its available member methods are right-valued only, which is intended to avoid problems with declarations like auto c=str[0]; then use c as a char_T
I then have to describe how the different methods affect the string class in order to explain the problem I'm having: for a single member access, string_t::arec is the fastest and doesn't trigger any memory copies, while the string_t::c_str function will cause possible memory copies at a much slower rate, and the string_t::unique_c_str is the one with the most overhead
With the preliminaries out of the way, here are the points of confusion.
In my tests today, a "range for" in the shape of for(char_t&c : str) is disabled because the function arec_t to char_T& is declared as explicit.
My attempt to remove explicit from arec_t::operator char_T& triggered other problems: the compiler reported an error for such a function because there are multiple overloading routes
char_t my_func(string_t<char_t>str){return str[rand()];}
And it seems that the compiler overloads char_t c=str[0] onto arec_t::operator char_T& instead of arec_t::operator char_T, where the former indirectly calls string_t::unique_c_str and the latter calls string_t::arec
I don't know how to modify the proxy class arec_t to do all that I want, including these:
- allow the "range for" of
string_taschat_T& - call
arec_t::operator char_Tinstead ofarec_t::operator char_T&when convertingarec_ttochar_Tbut only using the value to avoid unnecessary performance loss - in most cases (includes assignment, passing as a parameter, comparison)
arec_tbehaves like achar_Twithout the need for additional writing for conversion