I don't understand your problem with QString, but anyway, AFAIK the common way to get Unicode support with std::string is ICU. ICU is quite large (~15-20 MB binary) and it breaks binary compatibility with every release.
I don't do C++ anymore professionally, but from I remember, std::locale was very useful: https://en.cppreference.com/w/cpp/locale
And QString had no magic to it other than wrapping its underlying data in ytf-16 which std::u16string does too.
It's not perfect but it gets the job done, IIRC.
I used std::string and QString extensively for 4-5 years and find QString inexplicably bloated and un-intuitive for my use cases.