std::string
Classstd::string
, covering the most essential methods and operatorsIn this lesson, we’ll dive deeper into the std::string
objects we’ve been so heavily relying on.
We’ll investigate how they’re created, how they’re moved and copied, and some of the most valuable methods we have to interact with them.
std::string
solves many of the problems of C-style strings and is much easier to work with. So, we should prefer to use std::string
objects over C-style strings where possible.
The next few lessons will outline many of the reasons why.
std::string
The std::string
class and its associated utilities are available by including <string>
:
#include <string>
As we’ve seen, std::string
objects can be created from C-style strings. Typically, this is done from C-style string literals, but it doesn’t need to be:
#include <iostream>
#include <string>
int main(){
std::string StringA{"Hello"};
const char* CString{" World"};
std::string StringB{CString};
std::cout << StringA << StringB;
}
Hello World
We can go in the opposite direction, generating a C-style string from a std::string
using the c_str()
 method:
#include <iostream>
#include <string>
int main(){
std::string StringA{"Hello World"};
const char* CString{StringA.c_str()};
std::cout << CString;
}
Hello World
std::string
LiteralsThe standard library includes a way to create std::string
 literals.
To use them, we need to include a using
 statement:
using namespace std::string_literals;
In larger projects, statements like this would typically be part of a file that gets included in every other file.
Once we’ve implemented the using
statement, we can use std::string
literals in a similar way we use C-style strings.
We wrap them in double quotes, however, for std::string
we append an s
to the end:
#include <iostream>
#include <string>
using namespace std::string_literals;
int main(){
auto MyString{"Hello World"s};
std::cout << MyString;
if constexpr (std::same_as<
decltype(MyString), std::string>) {
std::cout << "\nThat was a std::string";
}
}
Hello World
That was a std::string
One of the main benefits of std::string
over C-style strings is that copying and moving them works in much the same way as any other modern data type.
We can deep copy a std::string
simply using the =
 operator:
#include <iostream>
#include <string>
int main(){
std::string StringA{" World"};
std::string StringB = StringA;
StringA = "Hello";
std::cout << StringA << StringB;
}
Hello World
Passing by value also creates a deep copy:
#include <iostream>
#include <string>
void Update(std::string Copy){
Copy = "Hello";
std::cout << Copy;
}
int main(){
std::string StringA{" World"};
Update(StringA);
std::cout << StringA;
}
Hello World
References (both l-value and r-value) work as they do with any other type:
#include <iostream>
#include <string>
using namespace std::string_literals;
void Log(std::string& LValue){
std::cout << "L-Value: " << LValue << '\n';
}
void Log(std::string&& RValue){
std::cout << "R-Value: " << RValue << '\n';
}
int main(){
std::string MyString{"Hello"};
Log(MyString);
Log("World"s);
std::string AnotherString{"Bye!"};
Log(std::move(AnotherString));
}
L-Value: Hello
R-Value: World
R-Value: Bye!
We cover l-values and r-values in our lesson on move semantics
If we want to get a pointer to a std::string
, we can do so in the usual way, using the address-of operator &
:
#include <iostream>
#include <string>
int main(){
std::string MyString{"Hello"};
std::string* Pointer{&MyString};
std::cout << Pointer << ": " << *Pointer;
}
0000004E9FBFF5A8: Hello
Another common way to pass std::string
objects in modern C++ is through a string view, which we’ll cover later in this chapter.
We can get the length of a std::string
using the length()
 method:
#include <iostream>
#include <string>
int main(){
std::string MyString{"Hello"};
std::cout << "Length: " << MyString.length();
}
Length: 5
std::string
objects also have the size()
method, which does the same thing. This is included for parity with other standard library containers such as std::vector
and std::array
, thereby making it easier to write templates.
We can check if two std::string
contain the same content using the ==
and !=
 operators:
#include <iostream>
#include <string>
int main(){
std::string Animal1{"Bear"};
std::string Animal2{"Bear"};
std::string Animal3{"Zebra"};
if (Animal1 == Animal2) {
std::cout <<
"Animal1 and Animal2 are equal";
}
if (Animal2 != Animal3) {
std::cout <<
"\nAnimal2 and Animal3 are not equal";
}
}
Animal1 and Animal2 are equal
Animal2 and Animal3 are not equal
std::string
objects also have the compare()
method. This returns an integer, which has the same meaning as the integer C-style string’s strcmp()
 function:
Here is an example:
#include <iostream>
#include <string>
int main(){
std::string Animal1{"Bear"};
std::string Animal2{"Bear"};
std::string Animal3{"Zebra"};
if (Animal1.compare(Animal2) == 0) {
std::cout <<
"Animal1 and Animal2 are equal\n";
}
if (Animal2.compare(Animal3) < 0) {
std::cout <<
Animal2 << " comes before " << Animal3;
}
}
Animal1 and Animal2 are equal
Bear comes before Zebra
std::string
objects also implement the usual comparison operators - <
, <=
, !=
, and so on:
#include <iostream>
#include <string>
int main(){
std::string Animal1{"Bear"};
std::string Animal2{"Bear"};
std::string Animal3{"Zebra"};
if (Animal1 == Animal2) {
std::cout <<
"Animal1 and Animal2 are equal\n";
}
if (Animal2 < Animal3) {
std::cout <<
Animal2 << " comes before " << Animal3;
}
}
Animal1 and Animal2 are equal
Bear comes before Zebra
Because the std::string
class implements all the comparison operators (or, since C++20, the spaceship operator), it can automatically compare the order of its objects.
Below, we sort a range of std::string
objects into alphabetical order:
#include <algorithm>
#include <string>
#include <iostream>
#include <vector>
using namespace std::string_literals;
int main(){
std::vector Animals{
"Bear"s, "Zebra"s, "Chicken"s,
"Alligator"s};
std::ranges::sort(Animals);
for (const std::string& Animal : Animals) {
std::cout << Animal << '\n';
}
}
Alligator
Bear
Chicken
Zebra
We can search our strings for specific substrings or individual characters using the find()
and rfind()
 methods.
find()
will return an integer representing the position where the first instance of the substring starts. rfind()
will return the position of the last instance.
If there aren’t multiple instances of the substring, find()
and rfind()
will return the same thing.
#include <string>
#include <iostream>
int main(){
std::string MyString(
"The cat slapped the other cat");
std::cout << "First cat starts at: " <<
MyString.find("cat");
std::cout << "\nSecond cat starts at: " <<
MyString.rfind("cat");
}
First cat starts at: 4
Second cat starts at: 26
If the substring is not found, these methods return a value that will pass an equality check with std::string::npos
:
#include <string>
#include <iostream>
int main(){
std::string String(
"The cat slapped the other cat");
if (String.find("dog") == std::string::npos) {
std::cout << "There is no dog in this fight";
}
}
There is no dog in this fight
The simpler starts_with()
, ends_with()
, and contains()
methods return true
if a string starts with, ends with, or contains a substring respectively.
Note: starts_with()
and ends_with()
were added in the C++20 specification, and contains()
in C++23. As a result, they may not yet be supported by your compiler.
#include <string>
#include <iostream>
int main(){
std::string String(
"The cat slapped the other cat");
if (String.starts_with("The")) {
std::cout << "It starts with \"The\"";
}
if (String.ends_with("cat")) {
std::cout << "\nIt ends with \"cat\"";
}
if (String.contains("slapped")) {
std::cout << "\nSomething got slapped";
}
}
It starts with "The"
It ends with "cat"
Something got slapped
For more complex use cases, a technique called regular expressions (regex) gives us significantly more flexibility in searching our strings.
We cover regular expressions later in this chapter.
We can generate a substring using the substr()
method, passing in the position we want the substring to start, and how many characters we want it to have:
#include <string>
#include <iostream>
int main(){
std::string String("Hello World");
std::cout << "First 5 characters: "
<< String.substr(0, 5);
}
First 5 characters: Hello
The second argument is optional, which means our substring will continue until the end of the original string:
#include <string>
#include <iostream>
int main(){
std::string String("Hello World");
size_t LastSpace{String.rfind(' ')};
std::cout << "Last word: "
<< String.substr(LastSpace + 1);
}
Last word: World
We can access any character in std::string
using the at()
or []
operators, passing in the index of the character we’re interested in:
#include <string>
#include <iostream>
int main(){
std::string String("Hello World");
std::cout << "The first character is "
<< String[0];
std::cout << "\nThe last character "
<< String.at(String.size() - 1);
}
The first character is H
The last character d
The at()
method will perform bounds-checking on the index we pass, to ensure it is valid.
The []
operator will skip this check. This has a minor performance gain, but will result in undefined behavior if the index is out of bounds.
#include <string>
#include <iostream>
int main(){
std::string String("Hello World");
String[100] = 42;
std::cout << "That was allowed, but dangerous";
try { String.at(100) = 42; }
catch (std::out_of_range& Error) {
std::cout << "\nThat was an exception:\n"
<< Error.what();
}
}
That was allowed, but dangerous
That was an exception:
invalid string position
We can generate new std::string
objects by concatenating other strings together, using the +
 operator:
#include <string>
#include <iostream>
int main(){
std::string StringA("Hello");
std::string StringB(" World");
std::string Combined{StringA + StringB};
std::cout << Combined;
}
Hello World
This also works with objects that can be converted to std::strings
, such as C-style strings and characters:
#include <string>
#include <iostream>
int main(){
std::string StringA("Hello");
std::string StringB("World");
std::string Combined{
StringA + " " + StringB + '!'};
std::cout << Combined;
}
Hello World!
Our next lesson covers a range of ways we can modify existing std::string
objects in place.
Often, we’ll have a std::string
object that contains a number, and we’ll want to convert it to a built-in numeric type. The most common needs can be met by:
std::stoi
- convert a std::string
to an int
std::stof
- convert a std::string
to a float
std::stod
- convert a std::string
to a double
#include <string>
#include <iostream>
int main(){
std::string Pi{"3.14"};
int PiInt{std::stoi(Pi)};
float PiFloat{std::stof(Pi)};
double PiDouble{std::stod(Pi)};
std::cout
<< PiInt * 2 << '\n'
<< PiFloat * 2 << '\n'
<< PiDouble * 2;
}
6
6.28
6.28
We can go the other way, converting a numeric type to a std::string
using the std::to_string()
 function:
#include <string>
#include <iostream>
int main(){
int Number{42};
std::string String{std::to_string(Number)};
std::cout << String;
}
42
In the next lesson, we’ll look at more properties and methods of std::string
, and how they can interact with the standard library algorithms.
In this lesson, we've explored the std::string
class, demonstrating how it simplifies string manipulation compared to C-style strings.
std::string
class.std::string
objects from C-style strings and using std::string
literals.std::string
objects.std::string
objects using operators and the compare()
method.std::string
using find()
, rfind()
, and C++20/23 methods like starts_with()
, ends_with()
, and contains()
.std::string
using the at()
method and []
operator.substr()
method.std::string
objects and converting between std::string
and numeric types.std::string
ClassA detailed guide to std::string
, covering the most essential methods and operators
Comprehensive course covering advanced concepts, and how to use them on large-scale projects.