STL 容器的 Const 锁包装器

Const lock wrapper for STL containers

我正在尝试为 std::vector(或来自 STL 的任何其他容器,如果可能的话)创建一个包装器,它可以 "lock" 和 "unlock" 向量的 const 状态它持有。

例如,如果我创建该包装器的对象,我希望能够执行如下操作:

int main()
{
    ConstLockVectorWrapper<int> myWrapper(std::vector<int>{});  // Here I pass an empty vector in the constructor parameters,
                                                                // which means that my wrapper will be holding an empty vector

                                                                // By default the vector inside my wrapper is not locked,
                                                                // I can change its size and the values that it holds

    myWrapper.get().push_back(10); // ok
    myWrapper.get().push_back(20); // ok
    myWrapper.get().at(0) = 5; // ok

    print(myWrapper.get()); // Prints 5 20



    myWrapper.lock(); // Now I made the vector inside my wrapper unchangable 



    myWrapper.get().push_back(30); // error, the vector is locked
    myWrapper.get().at(0) = 55; // error

    print(myWrapper.get()); // ok



    myWrapper.unlock(); // Now I can change my vector's size and its values again


    _getch();
    return 0;
}

我得到的唯一解决方案(不幸的是,这不起作用)是在包装器 [=48] 中创建一个常量引用 (const std::vector<T> &) 和一个常规引用​​ (td::vector<T> &) =],并将它们绑定到包装器 class.

中的主向量

所以,这就是我所做的:

template <typename T>
class ConstLockVectorWrapper {
public:
    ConstLockVectorWrapper(const std::vector<T> & vec)
        : wrappedVector(vec), wrappedVectorRef(wrappedVector), wrappedVectorConstRef(wrappedVector), constLock(false)
    {}

    void lock()
    {
        if (constLock) // if the vector is already locked, we just exit the function
            return;

        // else we lock the vector
        constLock = true;
    }

    void unlock()
    {
        if (!constLock) // if the vector is already unlocked (changable), we just exit the function
            return;

        // else we unlock the vector
        constLock = false;
    }

    return_type get() // I need to return a const std::vector<T> & if constLock == true, and std::vector<T> & otherwise, what return type should I put in here?
    {
        if (constLock)
            return wrappedVectorConstRef;
        else
            return wrappedVectorRef;
    }

private:
    bool constLock;
    std::vector<T> wrappedVector;

    // refs
    std::vector<T> & wrappedVectorRef;
    const std::vector<T> & wrappedVectorConstRef;
};

当然不行。只是因为我不知道在我的 get() 函数的 return 类型中放什么。

我试过使用尾随 return 类型,但没有用:

template <typename T>
class ConstLockVectorWrapper {
public:
    // ...

private:
    bool constLock;
    std::vector<T> wrappedVector;

    // refs
    std::vector<T> & wrappedVectorRef;
    const std::vector<T> & wrappedVectorConstRef;

public:
    auto get() -> decltype((constLock ? wrappedVectorConstRef : wrappedVectorRef)) 
    {
        if (constLock)
            return wrappedVectorConstRef;
        else
            return wrappedVectorRef;
    }
};

我想不出任何实际可行的解决方案,因为我还不太擅长 C++。

所以我请求你帮助解决我的问题。任何解决此问题的建议或提示将不胜感激!

谢谢

PS

我的主要目标是使我的包装器容器类型独立,因此它可以 "lock" 和 "unlock" 它所持有的容器的 const 状态,与其类型无关。

这是我在第一个代码片段中使用的 print() 函数:

template <typename Container>
void print(const Container & c)
{
    for (const auto & var : c)
        std::cout << var << std::endl;
}

从根本上说,一个方法总是return相同的东西。同一类型。每次。在 C++ 中,不可能有一种方法有时 return 一种类型,而在其他时候使用另一种类型。 C++ 不能这样工作。

因此,最初的方法是 get() return 具有状态的代理对象。使用大致相同的 类 和问题中的名称:

class return_type {

      bool is_const;
      std::vector<T> &wrapped_vec;

public:
      return_type(bool is_constArg,
                  std::vector<T> &wrapped_vecArg)
          : is_const(is_constArg), wrapped_vec(wrapped_vecArg)
      {
      }

      void push_back(T &&t)
      {
           if (is_const)
                throw std::runtime_error(); // Or, whatever...
           wrapped_vec.push_back(std::forward<T>(t));
      }

      // return_type will have to implement, and baby-sit all other
      // methods you wish to invoke on the underlying vector.
};

return_type get()
{
    return return_type(constLock);
}

这很简单,但是很粗糙而且有些乏味。您将必须实现在 return_type 代理中需要使用的每个 std::vector 方法。

更好的方法是利用 C++11 lambda。这将避免以一些额外的代码膨胀为代价重新实现每个轮子的需要。但是,大不了。如今,RAM 很便宜。您现在将在包装器中实现两个模板方法,而不是 get()return_typeget_const()get_mutable()。他们每个人都接受一个 lambda 参数并调用它,如果一切顺利,将包装的向量作为参数传递给它:

      template<typename lambda>
      void get_mutable(lambda &&l)
      {
           if (constLock)
                throw std::runtime_error(); // Or, whatever...

           l(wrapped_vec);
      }

      template<typename lambda>
      void get_const(lambda &&l)
      {
           l(const_cast<const std::vector<T> &>(wrapped_vec));
      }

您现在唯一需要决定的是您是需要访问可变向量还是常量向量,然后选择正确的 getter:

myWrapper.get_mutable( [&](std::vector<int> &v) { v.push_back(10); } );

get_mutable()如果此时vector被锁定则抛出异常。否则它会将向量传递给您的 lambda。你的 lambda 做任何它想做的事,可以是 push_back(),或者其他任何东西,然后是 returns.

但如果您只需要对矢量进行只读访问,请使用 get_const():

int s;

myWrapper.get_const( [&](const std::vector<int> &v) { s=v.size(); } );

请注意,在调用 lambda 之前,get_const() 会注意 const_cast 向量,因此 lambda 将无法修改它。这将在编译时强制执行。

通过一些额外的工作,也可以稍微清理一下,让 getter 也 return 任何 lambda return 给调用者,使得可以这样做:

int s=myWrapper.get_const( [&](const std::vector<int> &v) { return v.size(); } );

有可能让 get_const()get_mutable() 足够聪明地弄清楚 lambda return 是否有什么东西,并愉快地将它传回给调用者,无论它是什么。我想,如何做到这一点将是 whosebug.com

上的另一个问题

P.S。如果你没有 C++11,你可以只使用 get_const()get_mutable() return 包装向量(get_mutable() 验证它没有被锁定)。这确实完成了同样的事情。关键在于,由于 C++ 的工作方式,您必须提前明确需要常量访问还是可变访问。

不久前我也在研究类似的问题。在多线程环境中,有时根据您是在读还是在写,使用不同类型的锁会更有效。但是锁定是完全合作的。有可能获得 只读 锁,但仍然不小心写入对象。

我正在探索的一个解决方案是,不是从对象获取 只读锁,而是获取只读 wrapper我的对象,这样不仅对象 只读 被锁定,而且也只能调用 只读 (const ) 对象上的方法。

我使用的基本包装器是这样的:

template<typename T>
class ConstWrapper
{
    T& v;

public:
    ConstWrapper(T& v): v(v) {}

    T const& operator* () const { return v; } // return const reference
    T const* operator->() const { return &v;} // return const pointer
};

通过重载 *-> 运算符,您可以获得一种 传递 调用封闭对象方法的能力 - 但使用指针语义 (虽然它不是一个指针)。

std::vector<int> v {1, 2, 3, 4}; // not const

ConstWrapper<std::vector<int>> cv(v); // const wrapper

std::cout << cv->at(0) << '\n'; // okay at() is a const method

cv->push_back(8); // ILLEGAL!! push_back() is not a const method