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:
- When assigning the result of
std::make_format_args
to a variable of type std::basic_format_args
, it will always
dangle.
- When assigning the result of
std::make_format_args
to a variable of type auto
, it will dangle only 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.
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);
}
int main() {
helloAndGoodByeReality();
helloAndGoodByeExpectation();
helloAndGoodByeForRemote();
}
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
}
int main() {
helloAndGoodByeReality();
helloAndGoodByeExpectation();
helloAndGoodByeForRemote();
}