std::wstring vs std::string: which to choose for Unicode?

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: two strings, one headache

\n\n

Every C++ developer sooner or later faces a dilemma: std::string or std::wstring? They both seem like strings, both from the STL, but they behave completely differently. This question becomes especially acute when working with multilingual text, emoji, or hieroglyphs.

\n\n

If you are writing in C++ and want your program to correctly handle "Hello, world!" in Russian, Japanese, or Arabic — you cannot do without understanding the difference between std::string and std::wstring. In this article, we will break down both approaches, compare them by key parameters, and decide when to use which.

\n\n
This material is based on a real question from Stack Overflow that gathered thousands of views. We have adapted it for educational purposes, adding examples and a clear structure.
\n\n

std::string: classic and simple

\n\n

std::string is an alias for std::basic_string<char>. Each character occupies 1 byte. Historically, char was used for ASCII characters, but today it most often stores data in UTF-8 encoding (or just bytes).

\n\n

Pros of std::string

\n
    \n
  • Memory efficiency — 1 byte per character for ASCII/UTF-8.
  • \n
  • Compatibility — works with most libraries, including C functions (strlen, strcpy).
  • \n
  • Speed — less overhead on allocation.
  • \n
  • Ubiquity — the de facto standard for data transfer over the network and in JSON.
  • \n
\n\n

Cons of std::string

\n
    \n
  • Encoding confusion — std::string does not know in which encoding the data is stored. You can store UTF-8 in it, but .size() will return the number of bytes, not characters.
  • \n
  • Unicode issues — iterating over a string can break a multibyte character.
  • \n
  • No built-in support for wide characters — to work with Unicode outside of UTF-8, you will need to use third-party libraries.
  • \n
\n\n
// Example: std::string with UTF-8\n#include <iostream>\n#include <string>\n\nint main() {\n    std::string s = "Привет, мир!";\n    std::cout << "Length in bytes: " << s.size() << std::endl; // 21 bytes\n    std::cout << "First character (as char): " << s[0] << std::endl; // Garbage: first byte of UTF-8\n    return 0;\n}
\n\n

In this example, s[0] will return not 'П', but the first byte of the two-byte character 'П' (0xD0). This is a common beginner mistake.

\n\n

std::wstring: the power of wide characters

\n\n

std::wstring is std::basic_string<wchar_t>. The size of wchar_t depends on the platform: 2 bytes on Windows (UTF-16) and 4 bytes on Linux/macOS (UTF-32).

\n\n

Pros of std::wstring

\n
    \n
  • Fixed character size (on a specific platform) — simplifies working with Unicode.
  • \n
  • Direct locale support — on Windows, all WinAPI functions work with wchar_t.
  • \n
  • Fewer issues with multibyte characters — each character is one element of the string.
  • \n
\n\n

Cons of std::wstring

\n
    \n
  • Size depends on the platform — code can behave differently on Windows and Linux.
  • \n
  • Higher memory consumption — 2 or 4 bytes per character, even for ASCII.
  • \n
  • Compatibility issues — not all libraries support wchar_t.
  • \n
  • Surrogate pairs — in UTF-16, some characters (e.g., emoji) occupy 2 wchar_t units, which again creates the "character ≠ element" problem.
  • \n
\n\n
// Example: std::wstring on Windows\n#include <iostream>\n#inclu                

Blogs

Book Recommendations