In C#, there’s a convenient feature using the underscore _
as a discard variable when one wants to explicitly ignore the result of an operation.
I’m currently looking for an equivalent feature or workaround in C++ that would allow me to discard specific values or save memory allocation when retrieving multiple values from a function.
Example:
Let’s say I have a function like this:
void readValues(float& temperature, float& humidity, float& dewpoint, float& tempTrend, float& humTrend) {
// Some operation to retrieve values
}
If I only need to know the temperature, and I do not have a temperature-specific function, how can I discard the other values in the most memory-efficient way?
Or any other approach that could help in conserving memory space when ignoring certain return values from functions in an Arduino/ESP32 environment?
What I’m doing now:
float temperature;
float discard;
readValues(temperature, discard, discard, discard, discard);
What I tried:
float temperature;
readValeus(temperature, null_ptr, null_ptr, null_ptr, null_ptr);
float temperature;
readValues(temperature, 0, 0, 0, 0);
Thank you for any insights or suggestions!
There are a few ways to do what you want.
There is no direct equivalent to the discard
keyword though.
Firstly, I wouldn’t worry about being memory-efficient in this case.
Even if you always returned all values, unused values and their creation may be optimized away.
Also, output parameters are typically frowned upon in C++. (see F.20: For “out” output values, prefer return values to output parameters) You can pack things into a struct
and then simply use the parts of it that you’re interested in. Anyhow, here are some possible solutions:
(1) Function overloading
void readValues(float& temperature, float& humidity, float& dewpoint);
void readValues(float& temperature, float& humidity);
The second overload makes dewpoint
effectively an optional parameter.
(2) Pointers and default parameters
void readValues(float& temperature, float& humidity, float* dewpoint = nullptr);
// ...
readValues(t, h); // don't provide dewpoint
readValues(t, h, &d); // provide dewpoint
(3) Avoid output parameters
This is my personal favorite.
struct Weather {
float temperature;
float humidity;
float dewpoint;
};
Weather readValues();
// ...
Weather w = readValues();
auto [t, h, d] = readValues();
auto [t, h, _] = readValues();
(4) [[maybe_unused]]
void readValues(float& temperature, float& humidity, float& dewpoint);
// ...
float t, h;
[[maybe_unused]] float _;
readValues(t, h, _);
You still have to provide an extra argument, but [[maybe_unused]]
expresses intent clearly and suppresses warnings about unused variables, if any.
It is trivial to implement some sort of _
surrogate providing object to store output value and doing nothing with it.
void readValues(float& temperature, float& humidity, float& dewpoint, float& tempTrend, int& counter) {
// Some operation to retrieve values
}
class t_Discarder final
{
public: template<typename x_Object>
/* IMPLICIT */ operator x_Object &(void) const
{
static x_Object s_object{};
return s_object;
}
};
t_Discarder _{};
int main()
{
float temperature{};
readValues(temperature, _, _, _, _);
}
However is it certainly not a good practice. In production code it would be better to supply properly named arguments and to explicitly mark them as unused or to invent a better query interface that does not require supplying so many arguments every time with most of them being discarded.
I recommend avoiding out parameters, instead use return values.