Suppose I have some object with an initial name such as quantities_of_widgets
.
std::vector<int> quantities_of_widgets = GetQuantities();
I then perform an operation on quantities_of_widgets
in-place. The vector quantities_of_widgets
no longer represents "a vector of some quantities of things called widgets".
PerformOperationInPlace(quantities_of_widgets);
After this operation, a more appropriate name for the variable would be weights_of_widgets
. That would certainly make the code more readable.
I can "change" the name by moving quantities_of_widgets
to a new vector weights_of_widgets
. This is a terrible idea. The move isn't free, it messes with memory continuity, etc.
std::vector<int> weights_of_widgets = std::move(quantities_of_widgets);
I could also document the meaning of the variable in comments. I might write
std::vector<int> widgets = GetQuantities(); // widgets represents quantities
PerformOperationInPlace(widgets); // widgets represents weights
UseWeights(widgets);
However, comments are less permanent and less readable. It becomes more difficult to keep track of the meaning of variables over multiple lines of code. If widgets
is used 20 lines down in the middle of a large expression, I'd have trouble documenting the meaning of the variable at that point.
SomeBigFunction(Func1(widgets, some_other_object_1), some_other_object_2);
I could try aliasing, but I want to make the old name invalid to help with maintainability. Is there a way to change the name of a variable with no run-time cost?
CodePudding user response:
Worrying about such small overhead is almost certainly not worth the effort in all but the most extreme scenarios. However, for the sake of completeness:
The only way to get truly overhead-free renaming of types with non-trivial destructors is via copy-elision.
In this context, you could make use of it via an immediately evaluated lambda and NRVO.
#include <vector>
std::vector<int> GetQuantities(int v);
void PerformOperationInPlace(std::vector<int>&);
void UseWeights(const std::vector<int>&);
template<typename T>
T rename(T&& rhs) {
return rhs;
}
void foo(int x) {
std::vector<int> quantities_of_widgets = GetQuantities(x);
PerformOperationInPlace(quantities_of_widgets);
UseWeights(quantities_of_widgets);
}
void bar(int x) {
std::vector<int> weights_of_widgets = [&]{
std::vector<int> quantities_of_widgets = GetQuantities(x);
PerformOperationInPlace(quantities_of_widgets);
return quantities_of_widgets;
}();
UseWeights(weights_of_widgets);
}
foo()
and bar()
compile down to effectively the exact same final assembly: (see on godbolt)
The only limitation is that the variable that will be renamed has to be declared within the lambda. Beyond that, implicit capture by reference makes the lambda effectively the same thing as a scope.
CodePudding user response:
It is best to document code by itself. Comments have tendency do degrade. So I would do this this way:
// name of this function terrible, it should be more like
// ExtractWeightsOfWidgetsFrom or something similar
std::vector<int> OperationInPlace(std::vector<int> data) {
// note argument is a copy!
....
return data;
}
void someCode() {
auto quantities_of_widgets = GetQuantities();
...
auto weights_of_widgets = PerformOperationInPlace(std::move(quantities_of_widgets));
// here `quantities_of_widgets` is empty since contents has been moved
// many tools will report an error if you use this value after that point
....
}
or even better I would just extract more functions:
void someCode() {
auto quantities_of_widgets = GetQuantities();
...
ProcesWeightsOfWidgets(
PerformOperationInPlace(std::move(quantities_of_widgets));
// in this version someCode ends here
}