如何强制调用 const find
How to force the const find to be called
我正在使用 C++11 中的 std::map
进行编码。
我读过 this link,它告诉我 C++11 标准保证对容器的 const 方法访问在不同线程中是安全的。
我的理解是说std::map::size()
是线程安全的,因为这个函数声明为size_type size() const noexcept;
.
现在我想线程安全地调用函数 std::map::find
。比如if (mp.find(xxx) != mp.end()) {}
. 但是[=15=有两个版本,一个是const
,一个是非const
:https://en.cppreference.com/w/cpp/container/map/find.
那么我怎么知道 find
调用的是哪个版本?如何强制调用常量版本 find
以获得线程安全代码?
我知道 std::map::cend()
有一个 const 版本,if (mp.find(xxx) != mp.cend()) {}
会按预期工作吗?
您本可以使用 std::as_const
。
if(std::as_const(mp).find(xxx)==mp.end())
{
//do your thing
}
如果 mp
是 const
,那么 find
将是 const
版本。但是,调用非常量版本在线程安全方面与调用 const
版本没有什么不同,如果您实际使用返回的迭代器对持有的值进行修改(如果您阅读了您链接的答案小心它包含这个区别)。
注意只有调用多个const
方法是线程安全的,同时从另一个线程调用非常量方法不是线程安全的。
一般规则是const
是多reader安全的,其他操作不是。
但也有一些例外。基本上,非 const
操作 returning 迭代器或对现有对象的引用(不创建对象)也被视为“reader”操作,并且是多重 reader安全的。使用他们 return 的非 const_iterator
修改基础数据通常会出现问题,但在许多容器中,如果在另一个操作正在访问的同一元素上进行此类修改,只会导致争用。
所以
if (map.find(foo) == map.end())
可以安全地与仅从 map
对象读取的其他操作一起使用。
仍然有充分的理由调用 const
操作。在 c++17 中有 std::as_const
,允许
if (std::as_const(map).find(foo) == map.cend())
仅调用 map
上的 const
方法。您可以轻松编写自己的 as_const
:
template<class T>
std::add_const_t<T>& as_const( T& t ) { return t; }
(在 c++11 中,您需要扩展 add_const_t
)。 std
版本增加了
template<class T>
void as_const( T const&& t ) = delete;
屏蔽一些比较病态的病例
...
当考虑线程安全时,意识到它是一个关系 属性。两个操作相对线程安全。
在std
个容器的情况下,您必须考虑操作如何读取或写入容器本身,它读取或写入哪些元素。虽然该标准更具技术性,但可以让您直观地了解允许的内容。
仅读取容器和 return 迭代器或元素引用的方法有时是非 const
,但它们本身仅读取容器。
改变或读取元素的操作通常只与改变该元素的其他操作互斥。在向量中,您可以自由 v[0] = 77;
而另一个线程读取 v[7]
没有同步(在 vector<bool>
之外)。但是当你 .push_back
容量不足时,你需要与其他所有读取同步。
这可能有违直觉。如果您正在对容器进行同步免费访问,请小心并阅读文档。直觉只是第一步。
我正在使用 C++11 中的 std::map
进行编码。
我读过 this link,它告诉我 C++11 标准保证对容器的 const 方法访问在不同线程中是安全的。
我的理解是说std::map::size()
是线程安全的,因为这个函数声明为size_type size() const noexcept;
.
现在我想线程安全地调用函数 但是[=15=有两个版本,一个是std::map::find
。比如if (mp.find(xxx) != mp.end()) {}
.const
,一个是非const
:https://en.cppreference.com/w/cpp/container/map/find.
那么我怎么知道 find
调用的是哪个版本?如何强制调用常量版本 find
以获得线程安全代码?
我知道 std::map::cend()
有一个 const 版本,if (mp.find(xxx) != mp.cend()) {}
会按预期工作吗?
您本可以使用 std::as_const
。
if(std::as_const(mp).find(xxx)==mp.end())
{
//do your thing
}
如果 mp
是 const
,那么 find
将是 const
版本。但是,调用非常量版本在线程安全方面与调用 const
版本没有什么不同,如果您实际使用返回的迭代器对持有的值进行修改(如果您阅读了您链接的答案小心它包含这个区别)。
注意只有调用多个const
方法是线程安全的,同时从另一个线程调用非常量方法不是线程安全的。
一般规则是const
是多reader安全的,其他操作不是。
但也有一些例外。基本上,非 const
操作 returning 迭代器或对现有对象的引用(不创建对象)也被视为“reader”操作,并且是多重 reader安全的。使用他们 return 的非 const_iterator
修改基础数据通常会出现问题,但在许多容器中,如果在另一个操作正在访问的同一元素上进行此类修改,只会导致争用。
所以
if (map.find(foo) == map.end())
可以安全地与仅从 map
对象读取的其他操作一起使用。
仍然有充分的理由调用 const
操作。在 c++17 中有 std::as_const
,允许
if (std::as_const(map).find(foo) == map.cend())
仅调用 map
上的 const
方法。您可以轻松编写自己的 as_const
:
template<class T>
std::add_const_t<T>& as_const( T& t ) { return t; }
(在 c++11 中,您需要扩展 add_const_t
)。 std
版本增加了
template<class T>
void as_const( T const&& t ) = delete;
屏蔽一些比较病态的病例
...
当考虑线程安全时,意识到它是一个关系 属性。两个操作相对线程安全。
在std
个容器的情况下,您必须考虑操作如何读取或写入容器本身,它读取或写入哪些元素。虽然该标准更具技术性,但可以让您直观地了解允许的内容。
仅读取容器和 return 迭代器或元素引用的方法有时是非 const
,但它们本身仅读取容器。
改变或读取元素的操作通常只与改变该元素的其他操作互斥。在向量中,您可以自由 v[0] = 77;
而另一个线程读取 v[7]
没有同步(在 vector<bool>
之外)。但是当你 .push_back
容量不足时,你需要与其他所有读取同步。
这可能有违直觉。如果您正在对容器进行同步免费访问,请小心并阅读文档。直觉只是第一步。