template < class T >
class temp_value {
public :
temp_value(T& var) : _var(var), _original(var) {}
~temp_value() { _var = _original; }
private :
T& _var;
T _original;
temp_value(const temp_value&);
temp_value& operator=(const temp_value&);
};
Ok, comme il semble que ce n'est pas aussi simple que je le pensais, voici une explication :
Dans son constructeur temp_value
stocke une référence à une variable et une copie de la valeur originale de la variable. Dans son destructeur, il restaure la variable référencée à sa valeur d'origine. Ainsi, peu importe ce que vous avez fait à la variable entre sa construction et sa destruction, elle sera réinitialisée lorsque l'objet temp_value
sort du champ d'application.
Utilisez-le comme ça :
void f(some_type& var)
{
temp_value<some_type> restorer(var); // remembers var's value
// change var as you like
g(var);
// upon destruction restorer will restore var to its original value
}
Voici une autre approche qui utilise l'astuce du garde-barrière :
namespace detail
{
// use scope-guard trick
class restorer_base
{
public:
// call to flag the value shouldn't
// be restored at destruction
void dismiss(void) const
{
mDismissed = true;
}
protected:
// creation
restorer_base(void) :
mDismissed(false)
{}
restorer_base(const restorer_base& pOther) :
mDismissed(pOther.is_dismissed())
{
// take "ownership"
pOther.dismiss();
}
~restorer_base(void) {} // non-virtual
// query
bool is_dismissed(void) const
{
return mDismissed;
}
private:
// not copy-assignable, copy-constructibility is ok
restorer_base& operator=(const restorer_base&);
mutable bool mDismissed;
};
// generic single-value restorer, could be made
// variadic to store and restore several variables
template <typename T>
class restorer_holder : public restorer_base
{
public:
restorer_holder(T& pX) :
mX(pX),
mValue(pX)
{}
~restorer_holder(void)
{
if (!is_dismissed())
mX = mValue;
}
private:
// not copy-assignable, copy-constructibility is ok
restorer_holder& operator=(const restorer_holder&);
T& mX;
T mValue;
};
}
// store references to generated holders
typedef const detail::restorer_base& restorer;
// generator (could also be made variadic)
template <typename T>
detail::restorer_holder<T> store(T& pX)
{
return detail::restorer_holder<T>(pX);
}
C'est juste un peu plus de code passe-partout, mais cela permet une utilisation plus propre :
#include <iostream>
template <typename T>
void print(const T& pX)
{
std::cout << pX << std::endl;
}
void foo(void)
{
double d = 10.0;
double e = 12.0;
print(d); print(e);
{
restorer f = store(d);
restorer g = store(e);
d = -5.0;
e = 3.1337;
print(d); print(e);
g.dismiss();
}
print(d); print(e);
}
int main(void)
{
foo();
int i = 5;
print(i);
{
restorer r = store(i);
i *= 123;
print(i);
}
print(i);
}
Cela lui enlève cependant la possibilité d'être utilisé dans une classe.
Voici une troisième façon d'obtenir le même effet (qui ne souffre pas des problèmes liés à la possibilité de lancer des destructeurs) :
Mise en œuvre :
//none -- it is built into the language
Utilisation :
#include <iostream>
template <typename T>
void print(const T& pX)
{
std::cout << pX << std::endl;
}
void foo(void)
{
double d = 10.0;
double e = 12.0;
print(d); print(e);
{
double f(d);
double g(e);
f = -5.0;
g = 3.1337;
print(f); print(g);
e = std::move(g);
}
print(d); print(e);
}
int main(void)
{
foo();
int i = 5;
print(i);
{
int r(i);
r *= 123;
print(r);
}
print(i);
}