C++ has evolved over time, and with it the methods to convert an int
to a string.
I will provide a summary in this answer.
Note that some methods don't give you a std::string
directly, but a char*
. You can easily convert char*
to the former, and in some cases it's beneficial to avoid std::string
.
Comparison
The following table compares all options (only C++ standard options, no third-party libraries) from most recent to least recent.
Method |
Result |
Pros & Cons |
std::format c++20 |
std::string |
✔️ universal method (for formattable types) ✔️ supports common bases, and locale ❌ slow to compile ❌ slow (forward to std::vformat ) |
std::to_chars c++17 |
written to
char[] |
✔️ fast and zero overhead (no dynamic allocations) ✔️supports ANY base as run-time argument ❌ only works for fundamental types ❌ interface is not ergonomic |
std::to_string c++11 |
std::string |
✔️ concise and self-explanatory ✔️ zero overhead (if you need a std::string ) ❌ only works for fundamental types ❌ base 10 only |
std::ostringstream c++98 |
std::string |
✔️ universal method (for types with << operator) ✔️ considers locale (e.g. can change base) ❌ slow, and high overhead of streams |
std::sprintf c++98 |
written to
char[] |
✔️ smallest assembly output ✔️ supports some bases ✔️ compatible with C, unlike all other methods ❌ only works for fundamental types ❌ interface is not ergonomic ❌ no type safety |
Recommended Practice
Use std::to_string
if you just need to turn an int
into a decimal string. It's simple, elegant, and correct.
If you can't use std::to_string
, choose another option based on the features you need. Prefer more modern solutions like std::to_chars
over older solutions like std::sprintf
.
Examples
std::format
std::string d = std::format("{}", 100); // d = "100"
std::string h = std::format("{:#x}", 15); // h = "0xf"
std::to_chars
std::array<char, 5> a;
auto [ptr, ec] = std::to_chars(a.data(), a.data() + a.size(), 1234);
// a = {'1', '2', '3', '4', indeterminate} (no null terminator!)
// ptr points to 4, and ec == std::errc{}
// notice that there is no null terminator
std::string_view view(a.data(), ptr); // string_view doesn't require null terminators
std::string s(a.data(), ptr); // wrapping in a std:string kinda defeats the point
std::to_string
std::string d = std::to_string(100); // d = "100"
std::ostringstream
std::string d = (std::ostringstream() << 100).str(); // d = "100"
std::string h = (std::ostringstream() << std::hex << 15).str(); // h = "0xf"
Note: these one-liners rely on LWG 1203 (C++20), but recent compilers allow it in C++11 mode too. Update your compiler, or create a separate std::ostringstream stream
variable if it doesn't work.
sprintf
/ snprintf
char a[20];
sprintf(a, "%d", 15); // a = {'1', '5', '\0', ?, ?, ?, ...}
snprintf(a, sizeof(a), "%#x", 15); // a = {'0', 'x', 'f', '\0', ?, ?, ...}
std::string s = a;
itoa()
takes three parameters. – Maladapted(char)(num+48)
– Portaltoportal