在对象中传递函数和操作符调用

时间:2015-09-14 17:30:38

标签: c++ c++11 immutability

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

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
}

任何帮助将不胜感激。谢谢!

编辑:将静态断言更改为throw和exception

4 个答案:

答案 0 :(得分:2)

您尝试做的事情看起来相当困难,但更重要的是,过度复杂并且不必为您所做的事情做好准备。

基本上你正在尝试做的事情(如果我错了,请纠正我)是创建一个编译时检查你是否应该能够在给定时间修改一个对象。但是,c ++已经有了内置的方法。只需声明或传递您的对象为const或const&amp;,编译器将不允许您修改对象的不可变部分。当你想要能够修改它时,不用const就传递它。你甚至可以从const&amp; amp;常规&amp;当你想从代码中去,你可以直接将代码修改为代码,尽管我不推荐它。

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

答案 1 :(得分:1)

您可以创建一个通用的包装类,您可以将该函数转发到使用lambda来捕获对内部成员的引用。在这个例子中,我只是使用if语句来检查它是否被锁定&#34;如果是,那么我们只需修改副本。

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

答案 2 :(得分:0)

这完全无法实现。对源代码进行了一些简单的修改,说明了为什么这不起作用。

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
}

你需要彻底重新思考你的方法。

答案 3 :(得分:0)

下面是一个示例,说明了我要保护的内容

  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);

填充数据后,我不需要担心我的数组中的指针搞砸了。