我想在多个变量超出范围时重置它们。
目前,我有 1 个变量的解决方案,类似于此处答案中给出的:
template<typename T>
struct Context
{
T savedValue;
T& ref;
Context(T& value) : savedValue(value), ref(value) {}
~Context()
{
ref = savedValue;
}
};
#define CONCAT_(x,y) x##y
#define CONCAT(x,y) CONCAT_(x,y)
// __line__ to allow multiple
#define SAVE_CONTEXT(var) Context<decltype(var)> CONCAT(ctx, __LINE__)(var)
我正在寻找的是这样的:
template<typename... Ts>
struct Context
{
std::tuple<Ts...> savedValues; // OK
T& ref; // What should be the type here?
Context(Ts... values) : {} // How ?
~Context()
{
// ?
}
};
#define SAVE_CONTEXT(...) ... // ?
如果我正确理解了这个问题,你可以用非宏方法实现同样的目的。
#include <tuple>
#include <utility>
template<typename... Args> class Context
{
std::tuple<Args...> mSavedValues; // for values
std::tuple<Args&...> mRefs; // for references
public:
constexpr Context(Args&... values)
: mSavedValues(values...)
, mRefs(values...)
{}
constexpr ~Context() {
// reset to the initial values
mRefs = mSavedValues;
}
};
// template "saveContext" instead of the MACRO
template<typename... Args>
constexpr Context<Args...> saveContext(Args&... values) {
return Context<Args...>(values...);
}
你可以像这样使用它:
int main()
{
int a = 1;
float b = 2.2f;
{
auto ctx = saveContext(a, b);
a = 10;
b = 20.02f;
// a and b will be set to 10 and 20.02f
}
// a and b will be reset to their original values
// (1 and 2.2f) when going out of scope
}
它不会比您已有的更复杂。
template<typename... Ts> struct Context
{
std::tuple<Ts...> values;
std::tuple<Ts&...> refs;
Context(Ts&... ts) : values(ts...), refs(ts...) {}
~Context() { refs = values; }
};
您不需要任何宏。
Context save(x, y, z);
借助 CTAD,可以省略模板参数。
您可以使用范围防护装置来做到这一点。这里有一个草图:
#include <stdexcept>
#include <iostream>
#include <type_traits>
//-----------------------------------------------------------------------------
template<typename fn_t>
static constexpr bool is_void_noexcept()
{
return std::is_same_v<decltype(std::declval<fn_t>()()), void> &&
noexcept(std::declval<fn_t>()());
}
//-----------------------------------------------------------------------------
// very simple scope guard implementation
template<typename fn_t>
class scope_guard_t
{
public:
scope_guard_t(fn_t fn) :
m_active{ true },
m_fn{ fn }
{
static_assert(is_void_noexcept<fn_t>(), "function must return void and be noexcept");
}
void reset() noexcept
{
m_active = false;
}
~scope_guard_t()
{
if (m_active) m_fn();
}
private:
bool m_active;
fn_t m_fn;
};
//-----------------------------------------------------------------------------
void f(int& value)
{
scope_guard_t guard{ [&]() noexcept
{
value = 0; // <== here is your reset :)
std::cout << "scope guard called\n";
}};
value = 42;
std::cout << value << "\n";
}
int main()
{
int value{ 12 };
std::cout << value << "\n";
f(value);
std::cout << value << "\n";
return 0;
}
Context
可以简单定义。对于宏来说,Context
类型的参数很难提取,希望可以使用auto
。
template<typename... Ts>
struct Context {
std::tuple<Ts...> savedValues;
std::tuple<Ts&...> refs;
Context(Ts&... values)
: savedValues{values...}, refs{values...} {
}
~Context() {
refs = savedValues;
}
};
#define SAVE_CONTEXT( ... ) \
auto CONCAT( ctx, __LINE__ ) { Context{__VA_ARGS__} }
int main() {
int var1 = 1;
double var2 = 3.14;
char var3 = 'a';
{
SAVE_CONTEXT( var1, var2, var3 );
var1 = 3;
var2 = 2.72;
var3 = '0';
}
endl( std::cout << var1 << ' ' << var2 << ' ' << var3 ); // restored
}
这些响应都有一个(可能无法解决)缺陷,您必须为一个您永远不会再引用的对象命名。此处的示例使用
save
或 guard
。
为这些对象选择一个名称是一项微不足道但令人恼火的编码负担。