在对象中传递函数和运算符调用

Passing function and operator calls in object

我想制作一个 class,它允许我锁定对象以防止被修改。它本质上是一个带有指定锁定状态的布尔值的模板。由于是模板,我不会知道所有可以在内部对象上调用的方法,所以我需要一个方法来传递调用...

template<class T>
class const_lock
{
  public:
  const_lock() : my_lock(false) {}
  void set_const_lock(bool state) {my_lock = state;}

  // HOW TO IMPLEMENT SOMETHING LIKE THESE????
  //
  template<typename...Args >
  auto operatorANY_OPERATOR (Args...args)
 {
    if(my_lock != false)
       throw std::exception("Objected locked to modification");
    return my_value.ANY_OPERATOR(args);
 }

  template<typename...Args >
  auto operatorANY_CONST_OPERATOR (Args...args) const
 {
    return my_value.ANY_CONST_OPERATOR(args);
 }

  template<typename...Args >
  auto ANY_METHOD(Args...args)
 {
    if(my_lock != false)
       throw std::exception("Objected locked to modification");
    return my_value.ANY_METHOD(args);
 }

  template<typename...Args >
  auto ANY_CONST_METHOD(Args...args) const
 {
    return my_value.ANY_CONST_METHOD(args);
 }

  private:
    bool my_lock;
    T my_value;
}

int main()
{
  const_lock<std::vector<int>> v;
  v.push_back(5);
  v.push_back(7);
  v.set_const_lock(true);
  v.push_back(9); // fails compilation
  std::cout << v.at(1) << std::endl; // ok
}

如有任何帮助,我们将不胜感激。谢谢!

编辑:将静态断言更改为抛出和异常

您尝试做的事情看起来相当困难,但更重要的是,对于您尝试做的事情来说过于复杂且不必要。

本质上,您要做的(如果我错了请纠正我)是创建一个编译时检查,检查您是否应该能够在给定时间修改对象。然而,c++ 已经有一个内置的方法来做到这一点。只需将您的对象声明或传递为 const 或 const&,编译器将不允许您修改对象的非可变部分。当您希望能够修改它时,请在不使用 const 的情况下传递它。您甚至可以将它从 const& 转换为常规 & 当您想从无法直接修改它的代码转到可以直接修改的代码时,尽管我不推荐这样做。

编辑:刚刚看到关于无引用数组问题的评论。别担心!标准库支持引用包装器,它允许您实质上将引用存储在数组或其他任何地方。

您可以创建一个通用包装器 class,您可以将函数转发给使用捕获对内部成员的引用的 lambda。在这个例子中,我只是使用一个 if 语句来检查它是否是 "locked",如果是,那么我们只修改一个副本。

template<class T>
class const_lock
{
private:
    bool my_lock;
    mutable T my_value;
public:
    const_lock() : my_lock(false) {}
    void set_const_lock() { my_lock = true; }


    template<typename F>
    auto operator()(F f) const -> decltype(f(my_value))
    {
        if (my_lock)
        {
            T temp{my_value};  // make a copy
            return f(temp);
        }
        else 
            return f(my_value); // modify wrraped value
    }
};

int main()
{
    const_lock<std::string> cl;
    cl([](std::string& s) {
        s = "foobar";
    });
    cl([](std::string& s) {
        std::cout << s << std::endl;
    });
    cl.set_const_lock();
    cl([](std::string& s) {
        s = "we should still be foobar";
    });
    cl([](std::string& s) {
        std::cout << s;
    });
}

这是完全无法实现的。对您的源代码进行微不足道的修改即可说明为什么这行不通。

int main()
{
  const_lock<std::vector<int>> v;
  v.push_back(5);
  v.push_back(7);
  if (rand() % 2)
      v.set_const_lock(true);
  v.push_back(9); // fails compilation
  std::cout << v.at(1) << std::endl; // ok
}

您需要彻底重新考虑您的方法。

下面是一个示例,说明了我要尝试防范的内容

  class Node
  {
    public:
      Node(int id) : my_id(id) {}
      // . . . 
      int id() {return my_id;}

    private:
      int my_id;
      // . . .
  };

  class Grid
  {
    public:
      Grid() {}
      // . . . 

      void associate(Node* n) { my_nodes.push_back(n); }

    private:
      // . . .
      std::vector<Node*> my_nodes;
  };

  Node* find(std::vector<Node>& Nodes, int ID)
  {
    for(auto i=Nodes.begin(); i!=Nodes.end(); ++i)
    {
      if (i->id() == ID)
      {
        return &*i;
      }
    }
  }

  main()
  {
    std::vector<Node> Nodes;
    // fill Nodes with data


    Grid Array;
    Array.associate( find(Nodes,14325) );
    Array.associate( find(Nodes,51384) );
    Array.associate( find(Nodes,321684) );

    // . . .
    Nodes.push_back(Node(21616)); // this can invalidate my pointers in Array

  }

如果我能让我的节点可用

 const_lock<std::vector<Node>> Nodes;

然后打电话

 Nodes.set_const_lock(true);

填充数据后,我不必担心数组中的指针会弄乱。