4

在访问者上下文中,我需要在访问孩子之前临时设置一个变量,然后再恢复该变量。我正在使用以下代码,但我确信有一种更优雅和正确的方法可以做到这一点:

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

当临时赋值对象超出范围时,该变量将恢复为之前的值。有什么更好的方法来做到这一点?

4

3 回答 3

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=forTemporaryAssignment以使用法看起来像一个作业,但我没有想出任何好的东西。

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

TemporaryAssignment是合理的,但您可能不想operator=定义,因为以下含义:

t = otherTemporaryValue;

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

于 2012-11-14T17:13:18.750 回答
2

swap不是最好的解决方案,因为如果TYPE有一个std::string成员,崩溃问题也可能发生。我认为我们可以使用这样的一些方法:

char buffer[sizeof(TYPE)];
memcpy(buffer, &mVariable, sizeof(TYPE));
memcpy(&mVariable, &mOriginalValue, sizeof(TYPE));
memcpy(&mOriginalValue, buffer, sizeof(TYPE));
于 2012-11-15T07:53:05.547 回答
0

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

于 2012-11-14T18:18:57.020 回答