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_type
:get_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
我正在尝试为 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_type
:get_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