Why is this an issue?
std::make_format_args
and std::make_wformat_args
return objects containing an array of formatting arguments that can be
implicitly converted to std::basic_format_args
. The type of the returned object cannot be spelled; it can only be accessed through
auto
.
A formatting argument has reference semantics for non-built-in types and does not extend the lifetime of the passed arguments. It is your
responsibility to ensure that the arguments to std::make_format_args
and std::make_wformat_args
outlive their return value.
Specifically, be aware that:
- Assigning the result of
std::make_format_args
to a variable of type std::basic_format_args
will always dangle.
- Assigning the result of
std::make_format_args
to a variable of type auto
will dangle when the formatting arguments
contain an rvalue of a non-built-in type.
While it is possible to assign std::make_format_args
to a variable declared with auto
if all the formatting arguments are
built-in types or lvalues, it is suspicious and error-prone. That is why we recommend that the result of std::make_format_args
is only
used as an argument for formatting functions.
This rule detects when the result of std::make_format_args
or std::make_wformat_args
isn’t used as an argument.
How to fix it
Pass the result of std::make_format_args
or std::make_wformat_args
directly as an argument.
Code examples
Noncompliant code example
void helloAndGoodByeReality() {
// Noncompliant, dangles
std::format_args numberOfHellosAndGoodByes = std::make_format_args(1000, 1);
std::cout << vformat("Hello {0} times, and goodbyes {1} times :(\n", numberOfHellosAndGoodByes);
}
void helloAndGoodByeExpectation() {
// Noncompliant, error-prone but doesn't dangle due to built-in types
auto numberOfHellosAndGoodByes = std::make_format_args(1000, 1000000);
std::cout << vformat("Hello {0} times, and goodbyes {1} times :)\n", numberOfHellosAndGoodByes);
}
std::string getHellosForRemote() {
return "zero";
}
std::string getGoodbyesForRemote() {
return "zero";
}
void helloAndGoodByeForRemote() {
// Noncompliant, dangles; getHellosForRemote() is an rvalue of non-built-in type std::string
auto numberOfHelloAndGoodBye = std::make_format_args(getHellosForRemote(), getGoodbyesForRemote());
std::cout << vformat("Hello {0} times, and goodbyes {1} times :|\n", numberOfHelloAndGoodBye);
}
Compliant solution
void helloAndGoodByeReality() {
std::cout << vformat("Hello {0} times, and goodbyes {1} times :(\n",
std::make_format_args(1000, 1)); // Compliant
}
void helloAndGoodByeExpectation() {
std::cout << vformat("Hello {0} times, and goodbyes {1} times :)\n",
std::make_format_args(1000, 1000000)); // Compliant
}
std::string getHellosForRemote() {
return "zero";
}
std::string getGoodbyesForRemote() {
return "zero";
}
void helloAndGoodByeForRemote() {
std::cout <<
vformat("Hello {0} times, and goodbyes {1} times :|\n",
std::make_format_args(getHellosForRemote(), getGoodbyesForRemote())); // Compliant
}