String interpolation in C++26

This bite-sized article explores the latest Standard’s support for Python-style f-strings (having a literal f prefix) in C++. This may seem like a strange feature to add, but if you use Python, chances are you already find them invaluable when mixing (or interpolating) values, such as variables and expressions, with static output, such as descriptions. So without further ado, let’s jump straight into the syntax with some simple applications.

Say you have a variable i, and want to output it. The way to do this currently is:

std::println("Value of i is {}", i);

Or using “legacy” C++ streams:

std::cout << "Value of i is " << i << '\n';

With C++26 (specifically proposal P3412) we can do the following:

std::println(f"Value of i is {i}");

(For the speed-readers among you, the changes are that the whole format string is prefixed with f and the i parameter is moved to inside the braces, {}.)

So, in reality is it such a big deal? Yes, like all good new features, utilizing them in preference to older styles is natural and intuitive. You should know that as well as requiring library support, this is a rare language enhancement feature, utilizing the also-new reflection capabilities of C++. There is in fact no extra C++ header required to use string interpolation, although there is a brand-new constexpr type std::basic_formatted_string (note: take care not to confuse this with the similarly named std::basic_format_string.)

We mentioned that expressions can be used in place of simple variables, so here is a program (tested on Compiler Explorer’s Clang P3412 with flag -std=c++26) which produces visible output:

#include <print>
#include <cmath>

int main() {
    std::println(f"The square root of 2 is approx {powf(2.0f, 0.5f)}");
}

It’s important to be aware that the braces are not interpreted as positional parameters by std::println, since the whole entity is already formatted by the time it is passed to this function. Also, there is no .c_str() or even .data() method to use, so puts() and fprintf() cannot be employed. (In practice this isn’t a problem, std::print[ln] can use C or C++ streams, or for ultimate flexibility a std::basic_formatted_string can be assigned to a std::string for further processing.)

Of course, we already have string prefixes (u8, u, U, L and R"() so addition of an f prefix to the language is orthogonal and can be usefully combined with these, as in LfR"(...)" (although support does not yet appear to be complete in the compiler used for testing). Here is a program which uses a multi-line interpolated literal f-string:

#include <iostream>

int main() {
    int i = 42, j = 43;
    std::cout << fR"(To see the value of i use {{i}} to get: {i}
To see the value of j use {{j}} to get: {j}
)";
}

As shown above we can simply use double opening or closing braces to get a literal brace inserted into the output, which is:

To see the value of i use {i} to get: 42
To see the value of j use {j} to get: 43

That just about wraps things up for this article. You should have a good idea about how to use f-strings in your own code to make std::cout and even std::print (largely) obsolete. Look forward to string interpolation being integrated into Clang trunk (and then a release), probably followed by Visual Studio C++ and GCC.

Leave a comment