将临时修改的向量传递到函数

Pass a temporarily modified vector to a function

本文关键字:函数 向量 修改      更新时间:2023-10-16

在回溯问题中,我必须以在每个级别修改向量的方式将向量传递到函数:

void function(std::vector <int> vec){
    vec.push_back(0); // Initial modification
    function(vec);
    vec.pop_back(); // Return to previous state
    vec.push_back(1); // Second modification
    function(vec);
    return;
}

我省略了无关的代码。

有什么方法可以简化矢量修改,以便我不必以后pop_back?

您可以创建 guard 类,该类在破坏时自动调用 pop_back

template <typename T>
class push_pop_guard
{
private:
    std::vector<T>& _v;
public:
    template <typename Item>
    push_pop_guard(std::vector<T>& v, Item&& x) : _v{v} 
    { 
        _v.push_back(std::forward<Item>(x));
    }
    ~push_pop_guard()
    {
        _v.pop_back();
    }
};

用法示例:

void function(std::vector <int> vec)
{
    {
        push_pop_guard ppg{vec, 0};
        function(vec);
    }
    // ...
}

您可以将值传递给方法,然后将其推到向量:

void function(std::vector <int> vec, int element){
    vec.push_back(element);
    //Do stuff
    function(vec,0);
    function(vec,1);
    return;
}

我发现您所拥有的已经相当可读。我只能提高两点。

首先,首先是通过引用传递矢量,避免冗余副本。

其次,要维护回溯不变性,您仍然需要维护pop_back。但是您可以将其移至一个不太尴尬的地方:

void function(std::vector<int> &vec){
    vec.push_back(0); // Initial modification
    function(vec);
    vec.back() = 1;   // Second modification
    function(vec);
    vec.pop_back();   // Return to previous state
    return;
}

由于该函数会弹出它推出的整数,因此我们可以确定vec.back()是呼叫之间的整数。因此,您可以执行简单的作业,而不是另一对pop并推动。

template<class F>
struct scope_guard_t {
  std::optional<F> f;
  scope_guard_t( F in ):f(std::move(f)) {}
  scope_guard_t( scope_guard_t&& o ):
    f(std::move(o.f))
  {
    o.clear();
  }
  void abandon() { clear(); }
  void commit_early() { commit(); clear(); }
  ~scope_guard_t() { commit(); }
private:
  void commit() { if(f) (*f)(); }
  void clear() { f = std::nullopt; }
};
template<class F>
scope_guard_t<F> scope_guard( F in ) { return {std::move(in)}; }
template<class C, class T>
auto temp_push( C& c, T&& t ) {
  c.push_back( std::forward<T>(t) ); // do
  return scope_guard(
    [&]{ c.pop_back(); } // undo
  );
}
void function(std::vector <int> vec){
  {
    auto scope = temp_push( vec, 0 );
    function(vec);
  }
  {
    auto scope = temp_push( vec, 1 );
    function(vec);
  }
}

std::optional可以在C 14和之前的boost::optional替换。

scope_guard通常很有用。

我还会通过参考传递来替换通过值传递的vector;这可能会更有效,因为它避免了每次递归的重新分配。