Upgrading our code base from C++17 to C++20 using MSVC, Microsoft now finally enforces this rule.
The semantics for void foo(int& optOutParam = 0)
would be optOutParam
is an optional output parameter.
Often I see this concrete example, where multiple output parameters are required:
bool Initialise(Window& wnd, Context& ctxt, std::string& error = "") {
// ...
}
The return value indicates whether the function succeeded, while error contains the reason why it might have failed.
Unfortunately std::optional
is explicitly forbidden for references, or this would be a great solution, too.
std::optional<int&> opt; // compiler error
A good way to refactor is to actually have the function return all values and refrain from using reference parameters as output.
std::tuple<Window, Context, std::string> Initialise();
void modernCpp() {
auto[wnd, ctxt, errorMsg] = Initialise();
if(!errorMsg.empty()) {
exit(1);
}
}
void olderCpp() {
Window wnd;
Context ctxt;
std::string errorMsg;
std::tie(wnd, ctxt, errorMsg) = Initialise();
// or
tuple<Window, Context, std::string> result = Initialise();
}
Unfortunately this means having to refactor every call-site as well, which might be a huge job in large code bases.
To deal with that case I use an r-value overload, that discards the error message.
No call-site has to be changed then.
bool Initialise(Window& wnd, Context& ctxt, std::string& error) {
// default value removed
}
bool Initialise(Window& wnd, Context& ctxt, std::string&& discardedErrorMsg = "") {
// call overload above
return Initialise(wnd, ctxt, discardedErrorMsg);
}
This has one major draw-back, namely gathering the info for error message might be very costly.
Asking a database what went wrong, for example, may require another network trip.
Bear in mind though, that the original function has the same issue, so this is a great micro-optimisation chance.
For the vast majority of new functions with multiple out parameters, I use multiple return values.
WxWidgets style guide says "don't use templates" and they have good reasons
<- screw std::vector, i say – Quackenbush