用于STL容器的Const锁包装器

时间:2016-08-23 00:05:43

标签: c++ vector stl wrapper

我试图为std::vector(或STL的任何其他容器,如果可能的话)创建一个包装器,它可以锁定"并且"解锁"它所持有的向量的常量状态。

例如,如果我创建该包装器的对象,我希望能够做到这样的事情:

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

唯一的解决方案(不幸的是,我不能工作)是在一个内部创建一个const引用(const std::vector<T> &)和一个常规引用​​(td::vector<T> &)包装器类,并将它们绑定到包装器类中的主向量。

所以,这就是我所做的:

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()功能的返回类型中放入什么。

我尝试使用尾随返回类型,但没有工作:

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

我的主要目标是使我的包装容器类型独立,因此它可以锁定&#34;并且&#34;解锁&#34;它所持有的容器的常态,与其类型无关。

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

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

2 个答案:

答案 0 :(得分:3)

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

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

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 lambdas。这将避免重新实现每个轮子的需要,代价是一些额外的代码膨胀。但是,很重要。 RAM很便宜,如今。您现在将在包装器中实现两个模板方法,而不是get()return_type,而不是get_const()get_mutable() 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)); } myWrapper.get_mutable( [&](std::vector<int> &v) { v.push_back(10); } ); 。它们中的每一个都接受一个lambda参数并调用它,如果一切顺利,将包装的向量作为参数传递给它:

get_mutable()

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

push_back()
如果此时向量被锁定,

get_const()会抛出异常。否则它将向量传递给lambda。你的lambda做了它想要的任何东西,可以是int s; myWrapper.get_const( [&](const std::vector<int> &v) { s=v.size(); } ); ,或其他任何东西,然后返回。

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

const_cast

注意int s=myWrapper.get_const( [&](const std::vector<int> &v) { return v.size(); } ); 在调用lambda之前会关注get_const()向量,因此lambda将无法修改它。这将在编译时强制执行。

通过一些额外的工作,也可以稍微清理一下,让getter也返回lambda返回给调用者的任何东西,这样可以做到这样的事情:

get_mutable()

get_const()get_mutable()可以足够聪明地判断lambda是否返回了某些内容,并愉快地将其传递给调用者,无论它是什么。我想,如何做到这一点必须是stackoverflow.com上的另一个问题

P.S。如果你没有C ++ 11,你可以让get_mutable()<?php $rootCatId= Mage::app()->getStore()->getRootCategoryId(); $categories = Mage::getModel('catalog/category')->getCategories($rootCatId); $output= '<ul>'; foreach($categories as $category) { $cat = Mage::getModel('catalog/category')->load($category->getId()); $count = $cat->getProductCount(); $output .= '<li>' . '<a href="' . Mage::getUrl($cat->getUrlPath()) . '">' . $category->getName() . "</a>"; if ($category->hasChildren()) { $children = Mage::getModel('catalog/category')->getCategories($category->getId()); $array .= get_categories($children); } $output .= '</li>'; } echo $output . '</ul>'; ?> 返回包装的向量(document.getElementsByTagName('script')[0].setAttribute('data-url', window.location.href)验证它没有被锁定)。这真的完成了同样的事情。关键点在于,由于C ++的工作方式,您必须事先消除歧义,无论您是需要持续访问还是可变访问。

答案 1 :(得分:1)

我前段时间正在研究类似的问题。在多线程环境中,根据您是在阅读还是写作,有时可以更有效地使用不同类型的锁。但锁定是完全合作的。可以获得只读锁,但仍然会意外地写入对象。

我正在探索的一个解决方案是,不是从对象获取只读锁,而是获取对象的只读包装,这样不仅是对象只读已锁定,它也只能在对象上调用只读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