HOWTO · C++
How to Return a String From a Function in C++
Learn how to return std::string by value and when references, pointers, std::move, or C-style strings are appropriate.
On this page
For an ordinary C++ function, return a std::string by value. This gives the caller an independent string and lets C++ apply return-value optimization when possible. Use a reference only when returning an existing object whose lifetime is managed elsewhere, and use a pointer or C-style string only when an interface specifically calls for one. The examples below use C++11 or later.
Return a String by Value
Returning by value is the clearest default when a function creates or transforms a string. The caller receives its own string value and can store or change it independently. Returning a named local is normally efficient: compilers can construct the result directly in the caller’s destination through named return value optimization. If that optimization does not apply, std::string can be moved. Neither behavior requires adding std::move to the return statement.
Run this example:
#include <iostream>
#include <string>
std::string makeGreeting() {
std::string result = "Hello, C++!";
return result;
}
int main() {
std::string greeting = makeGreeting();
std::cout << greeting << '\n';
}
The function’s return type is std::string, and return result; returns the local value. The local variable ceases to exist when the function ends, but the returned string remains available to the caller. Returning a value is usually simpler and safer than arranging separate ownership of a dynamically allocated string.
Return a Reference to an Existing String
A reference return aliases an object that already exists; it does not create a new string. This can be useful when a function selects or exposes a string owned by a longer-lived object. The caller sees the same object, so changes through a non-const reference affect the original. Use a const std::string& when callers need to read without modifying it.
The referenced object must remain alive for every use of the returned reference. Never return a reference to a local variable: that variable is destroyed as the function exits, leaving the caller with a dangling reference. A reference parameter can be returned safely only when the caller keeps its argument alive, as in this example:
#include <iostream>
#include <string>
const std::string& sameString(const std::string& value) {
return value;
}
int main() {
std::string original = "Existing string";
const std::string& alias = sameString(original);
std::cout << alias << '\n';
}
Return a String by Pointer
A pointer return can express optional access or follow an existing interface, but it makes lifetime and ownership part of the contract. Prefer returning the address of an existing string only when that string outlives every use of the pointer. Document who owns the object and whether the pointer may be null.
Do not allocate a std::string with new solely to return it. That forces the caller to remember to delete it and makes early returns and errors harder to manage. For a newly created string, return by value. The example below points to an existing object and keeps that object alive while the pointer is used:
#include <iostream>
#include <string>
const std::string* findGreeting(const std::string& value) {
return &value;
}
int main() {
std::string greeting = "Hello through a pointer";
const std::string* result = findGreeting(greeting);
if (result != nullptr) {
std::cout << *result << '\n';
}
}
The function does not take ownership of value. The caller must not keep the returned pointer after the referenced string has been destroyed.
Use std::move Only When It Helps
std::move is a cast that permits a move operation; it does not move data by itself. In a return statement, writing return std::move(local); is usually unnecessary. It can prevent named return value optimization, potentially replacing direct construction with a move. Prefer return local; for a local result and let the language and compiler choose the eligible optimization.
Explicit moves are useful when transferring from an object that is not eligible for automatic move treatment, such as a member that must be moved out of a longer-lived object. That changes the source object, so use it only when the source is no longer needed in its prior state. Do not add std::move just to make a return appear faster.
Return a C-Style String with char*
A C-style string is a null-terminated character sequence, not a std::string. If an API requires a character pointer and the result is fixed text, return const char* pointing to a string literal. String literals have static storage duration, so the pointer remains valid throughout the program. Here, const char* means a pointer to const characters: the characters cannot be modified through the pointer, but the pointer itself is not const and can be reassigned. This is different from char* const, which is a const pointer to modifiable characters. The string literal must not be modified.
#include <iostream>
const char* greeting() {
return "Hello from C-style text";
}
int main() {
std::cout << greeting() << '\n';
}
Do not return a pointer to a local character array, which stops existing when the function returns, or a pointer into a local std::string. The c_str() result is tied to the std::string object’s lifetime and can be invalidated by operations that modify that string. If a caller needs an independently owned, changeable result, return std::string instead.