在C ++中恢复临时赋值

时间:2012-11-14 17:09:42

标签: c++ variable-assignment

在访问者上下文中,我需要在访问子项之前临时设置变量,然后再恢复该变量。我正在使用以下代码,但我确信有更优雅和正确的方法来执行此操作:

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;
    }
};

这允许我写下类似的内容:

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

当临时赋值对象超出范围时,该变量将恢复为其先前的值。有什么更好的方法呢?

3 个答案:

答案 0 :(得分:6)

除了析构函数可以抛出之外,对我来说还不错,这很糟糕。 swap具有原始值而非分配(编辑:此处理std::string,但请查看与string用户不太友好的类可能出现问题的注释。

如果你从代码的这一部分稍微退一步,也许你可以找到一种不需要设置临时值的方法。对象中的共享可变状态可能是坏的,原因与可变全局变量是坏的相同,但程度较小,因为它只会弄乱你的类,而不是弄乱你的整个程序。

例如,您可以复制整个对象,为变量设置新值并访问副本而不是访问自己。显然,这不一定是可能的或有效的,你必须根据具体情况寻找替代方案。就孩子们而言,也许副本可能很浅(即引用相同的子对象),这可能足以使其便宜。

关于使用情况,您可以推断出类似这样的类型(未经测试的代码):

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

用法:

auto t = temp_value(myVariable, myTemporaryValue);

然后你需要一个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;
};

我想过为operator=指定TemporaryAssignment,以便使用看起来像是一项任务,但我没有提出任何好处。

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

似乎是合理的,但您可能不希望TemporaryAssignment定义operator=,因为其含义为:

t = otherTemporaryValue;

不一定清楚,可能不应该被允许。也许从temporary返回第二个类,并从TemporaryAssignment返回operator=

答案 1 :(得分:2)

swap不是最佳解决方案,因为如果TYPEstd::string成员,则崩溃问题也可能发生。 我想我们可以使用这样的方法:

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

答案 2 :(得分:0)

补充Steve Jessop的答案。从RIIA(或RAII)的角度来看,类的析构函数可能最终获取资源(内存),它应该只释放资源。为了避免这种情况,您可能会想到使用一个在正确的时间指向正确对象的myVar,因此只能在构造或获取时正确创建新对象,并且在发布时只会发生删除和指针重新分配