Returning temporary assignment in C ++

In the visitorโ€™s context, I need to temporarily set a variable before visiting the children and return this variable again. I use the following code, but I'm sure there is a more elegant and correct way to do this:

template <typename TYPE> class TemporaryAssignment { protected: TYPE& mVariable; TYPE mOriginalValue; public: TemporaryAssignment(TYPE& inVariable, TYPE inValue) : mVariable(inVariable), mOriginalValue(inVariable) { mVariable = inValue; } ~TemporaryAssignment(void) { mVariable = mOriginalValue; } }; 

This allows me to write something like the following:

 { ... TemporaryAssignment<string> t(myVariable, myTemporaryValue); visitChildren(); ... } // previous value of myVariable is restored 

The variable will revert to its previous value when the temporary object goes out of scope. What is the best way to do this?

+4
source share
3 answers

It seems to me that the destructor can throw, which is bad. swap with the original value instead of the destination (edit: this deals with std::string , but see comments for possible problems with classes that are less user friendly than string ).

If you step back a bit from this part of the code, you might be able to find a way to not set a temporary value at all. A shared changed state in an object can be bad for the same reason that changed global variables are bad, but to a lesser extent, because it will only ruin your class, and not ruin your entire program.

For example, perhaps you could copy the entire object, set a new value for the variable, and visit the copy, rather than visit yourself. Obviously, this is not necessarily possible or effective; you need to look for alternatives on an individual basis. Perhaps the copy may be shallow, as far as children are concerned (i.e., referring to the same child objects), which may be enough to make it cheap.

As for usage, you can infer a type like this (unverified code):

 template <typename T, typename ARG> TemporaryAssignment<T> temp_value(T &var, ARG &&newvalue) { return TemporaryAssignment(var, std::forward<ARG>(newValue)); } 

Using:

 auto t = temp_value(myVariable, myTemporaryValue); 

Then you need the move constructor for TemporaryAssignment:

 template <typename TYPE> class TemporaryAssignment { // change data member TYPE *mVariable; TYPE mOriginalValue; public: TemporaryAssignment(TYPE &inVariable, TYPE inValue) : mVariable(&inVariable), mOriginalValue(std::move(inVariable)) { *mVariable = std::move(inValue); } TemporaryAssignment(TemporaryAssignment &&rhs) { mOriginalValue = std::move(rhs.mOriginalValue); mVariable = rhs.mVariable; rhs.mVariable = 0; } ~TypeAssignment() { using std::swap; if (mVariable) { swap(*mVariable, mOriginalValue); } } // can't remember whether this is needed TemporaryAssignment(const TemporaryAssignment &) = delete; TemporaryAssignment &operator=(const TemporaryAssignment &) = delete; TemporaryAssignment &operator=(TemporaryAssignment &&) = delete; }; 

I thought a bit about specifying operator= for TemporaryAssignment to make use look like an assignment, but I didn't come up with anything good.

 auto t = (temporary(myVariable) = myTemporaryValue); 

plausible, but you probably don't want the TemporaryAssignment define operator= , because the value is:

 t = otherTemporaryValue; 

not necessarily clear and probably should not be allowed. Maybe with a second class that will be returned from temporary , and which returns a TemporaryAssignment from its operator= .

+6
source

swap not the best solution, because if TYPE has a member of std::string , there may be a problem with the failure. I think we can use some methods like this:

 char buffer[sizeof(TYPE)]; memcpy(buffer, &mVariable, sizeof(TYPE)); memcpy(&mVariable, &mOriginalValue, sizeof(TYPE)); memcpy(&mOriginalValue, buffer, sizeof(TYPE)); 
+2
source

Add a response from Steve Jessop. From the point of view of the RIIA (or RAII), the destructor of your classes can ultimately get a resource (memory), where it should only free resources. To avoid this, you could probably consider using myVar, which points to the right object at the right time, so new objects can only be correctly created at build time or collection time, and only pointer deletion and reassignment will happen during release.

0
source

Source: https://habr.com/ru/post/1446047/


All Articles