超出范围时如何重置多个变量?

问题描述 投票:0回答:5

我想在多个变量超出范围时重置它们。

目前,我有 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(...) ...       // ?
c++ templates scope c++17 class-template
5个回答
2
投票

如果我正确理解了这个问题,你可以用非宏方法实现同样的目的。

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

在 godbolt.org 中观看现场演示


2
投票

它不会比您已有的更复杂。

  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,可以省略模板参数。


0
投票

您可以使用范围防护装置来做到这一点。这里有一个草图:

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

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
}

0
投票

这些响应都有一个(可能无法解决)缺陷,您必须为一个您永远不会再引用的对象命名。此处的示例使用

save
guard

为这些对象选择一个名称是一项微不足道但令人恼火的编码负担。

© www.soinside.com 2019 - 2024. All rights reserved.