以下是Scope Guard的惯用C ++ 11实现,它会在范围退出时恢复值吗?
template<typename T>
class ValueScopeGuard
{
public:
template<typename U>
ValueScopeGuard(T& value, U&& newValue):
_valuePtr(&value),
_oldValue(std::forward<U>(newValue))
{
using std::swap;
swap(*_valuePtr, _oldValue);
}
~ValueScopeGuard()
{
if(_valuePtr)
{
using std::swap;
swap(*_valuePtr, _oldValue);
}
}
// Copy
ValueScopeGuard(ValueScopeGuard const& other) = delete;
ValueScopeGuard& operator=(ValueScopeGuard const& other) = delete;
// Move
ValueScopeGuard(ValueScopeGuard&& other):
_valuePtr(nullptr)
{
swap(*this, other);
}
ValueScopeGuard& operator=(ValueScopeGuard&& other)
{
ValueScopeGuard(std::move(other)).swap(*this);
return *this;
}
private:
T* _valuePtr;
T _oldValue;
friend void swap(ValueScopeGuard& lhs, ValueScopeGuard& rhs)
{
using std::swap;
swap(lhs._valuePtr, rhs._valuePtr);
swap(lhs._oldValue, rhs._oldValue);
}
};
template<typename T, typename U>
ValueScopeGuard<T> makeValueScopeGuard(T& value, U&& newValue)
{
return {value, std::forward<U>(newValue)};
}
它可用于临时更改值,如下所示:
int main(int argc, char* argv[])
{
// Value Type
int i = 0;
{
auto guard = makeValueScopeGuard(i, 1);
std::cout << i << std::endl; // 1
}
std::cout << i << std::endl; // 0
// Movable Type
std::unique_ptr<int> a{new int(0)};
{
auto guard = makeValueScopeGuard(a, std::unique_ptr<int>{new int(1)});
std::cout << *a << std::endl; // 1
}
std::cout << *a << std::endl; // 0
return 0;
}
像这样的简单实用程序是否已在某个库中实现?我看了一下Boost.ScopeExit,但它的用途似乎不同而且更复杂。
答案 0 :(得分:3)
假设makeValueScopeGuard
实现为:
template< typename T >
ValueScopeGuard<T> makeValueScopeGuard( T& t, T&& v )
{
return ValueScopeGuard<T>(t,std::move(v));
}
不,它不是很好的范围保护实现,因为当你传递l值作为第二个参数时它会失败:
int kk=1;
auto guard = makeValueScopeGuard(i, kk);
第二个问题是,当您使用std::forward
时,您使用了std::move
。
如this question and answers所示,人们通常使用lambdas来实现范围保护。
答案 1 :(得分:1)
你的移动构造函数使指针成员保持未初始化状态,因此rvalue对象最终会持有一个垃圾指针,它会在析构函数中取消引用。那是一个错误。您应该将其初始化为nullptr
并在析构函数中检查nullptr
。
对于这样的类型我不希望移动赋值是一个简单的交换,我希望rvalue最终不会拥有任何东西。所以我会实现这样的移动,所以rvalue最终为空:
ValueScopeGuard& operator=(ValueScopeGuard&& other)
{
ValueScopeGuard(std::move(other)).swap(*this);
return *this;
}
名称makeValueScopeGuard
我不清楚它会更改值本身,我希望它只是复制当前值并在析构函数中恢复它。
就现有类型而言,我能想到的最接近的是Boost I/O state savers,它不会改变当前状态,只是复制它并恢复它。