遍历和擦除 std::map 中的某些元素时出现分段错误?
Segmentation fault while iterating through and erasing some elements from std::map?
我正在使用以下几个 std::map
类型的数据容器:
std::map<int, std::vector< cv::Point > > mapGoodContours;
std::map<int, EllipseProperties> ellipsePropertiesMap;
std::map<int, float> m_markerRadiusMap;
std::map<int,cv::Point2f> markers;//This is part of another class
我遍历这些容器并在这些元素满足特定条件后删除一些元素,如以下代码所示。
auto radiusMapCounter = m_markerRadiusMap.begin();
auto markerCounter = frames.markers.begin();
auto goodContoursCounter = mapGoodContours.begin();
if(m_markerRadiusMap.size()==ellipsePropertiesMap.size() && frames.markers.size()==ellipsePropertiesMap.size()
&& mapGoodContours.size()==ellipsePropertiesMap.size())
{
for(auto ellipsePropertyCounter = ellipsePropertiesMap.begin(); ellipsePropertyCounter != ellipsePropertiesMap.end(); ellipsePropertyCounter++)
{
float upperLimit = (float)m_upperFactor*(float)ellipsePropertyCounter->second.radius;
float lowerLimit = (float)m_lowerFactor*(float)ellipsePropertyCounter->second.radius;
if(ellipsePropertyCounter->second.minDistanceFromOtherEllipse>upperLimit
|| ellipsePropertyCounter->second.minDistanceFromOtherEllipse<lowerLimit)
{
ellipsePropertiesMap.erase(ellipsePropertyCounter);
m_markerRadiusMap.erase(radiusMapCounter);
frames.markers.erase(markerCounter);
mapGoodContours.erase(goodContoursCounter);
}
else
{
smallContours.push_back(goodContoursCounter->second);
}
radiusMapCounter++;
markerCounter++;
goodContoursCounter++;
}
}
我很困惑地发现有时我会出现如图所示的分割错误。 故障具体指向代码为radiusMapCounter++;
的那一行
我做错了什么?
从容器中删除迭代器指向的元素后,您无法递增迭代器。创建迭代器的副本,递增它,通过复制删除元素。
如果您使用的是 C++11 或更高版本,std::map::erase(...)
returns 最后一个删除元素之后的迭代器,因此您可以使用它而不是递增无效元素。在这种情况下,无需创建要在 erase
中使用的迭代器副本。
for(auto ellipsePropertyCounter = ellipsePropertiesMap.begin();
ellipsePropertyCounter != ellipsePropertiesMap.end();
/* do not increment iterator here */)
{
float upperLimit = (float)m_upperFactor*(float)ellipsePropertyCounter->second.radius;
float lowerLimit = (float)m_lowerFactor*(float)ellipsePropertyCounter->second.radius;
if(ellipsePropertyCounter->second.minDistanceFromOtherEllipse>upperLimit
|| ellipsePropertyCounter->second.minDistanceFromOtherEllipse<lowerLimit)
{
ellipsePropertyCounter = ellipsePropertiesMap.erase(ellipsePropertyCounter);
radiusMapCounter = m_markerRadiusMap.erase(radiusMapCounter);
markerCounter = frames.markers.erase(markerCounter);
goodContoursCounter = mapGoodContours.erase(goodContoursCounter);
}
else
{
smallContours.push_back(goodContoursCounter->second);
radiusMapCounter++;
markerCounter++;
goodContoursCounter++;
ellipsePropertyCounter++; // increment loop iterator only in 'else' case
}
}
这是一个 C++17 解决方案。
第一个 indexer
,它是 C++14,允许您在使用时无需特殊用途的助手即可内联创建和解压索引包:
template<class=void,std::size_t...Is>
void indexer( std::index_sequence<Is>... ) {
return [](auto&& f) {
using discard=int[];
(void)discard{0,((
f( std::integral_constant<std::size_t, Is>{} )
),void(),0)...};
};
}
template<std::size_t N>
void indexer() {
return indexer( std::make_index_sequence<N>{} );
}
现在,erase_if
,SFINAE 应将其限制为仅适用于关联(可能还有其他基于节点的)容器:
template<class Test, class...Maps>
bool erase_if( Test&& test, Maps&&...maps) {
using std::begin; using std::end;
std::tuple< decltype(begin(maps))... > its;
std::tuple< decltype(begin(maps))... > ends;
auto m_tup = std::tie(maps...);
auto index = indexer<sizeof...(maps)>;
index(
[&](auto i){
std::get<i>(its) = begin( std::get<i>(m_tup) );
std::get<i>(ends) = end( std::get<i>(m_tup) );
}
);
while (its != ends) {
auto deref_then_test = [&test](auto...its) {
return test( (*its)... );
};
if (std::apply(deref_then_test,its)) {
index(
[&](auto i){
std::get<i>(its) = std::get<i>(m_tup).erase(std::get<i>(its));
}
);
} else {
index(
[&](auto i){++std::get<i>(its);}
);
}
}
}
这让您可以根据对其中一个容器的测试从多个容器中擦除。
代码未经测试,可能包含拼写错误。
我正在使用以下几个 std::map
类型的数据容器:
std::map<int, std::vector< cv::Point > > mapGoodContours;
std::map<int, EllipseProperties> ellipsePropertiesMap;
std::map<int, float> m_markerRadiusMap;
std::map<int,cv::Point2f> markers;//This is part of another class
我遍历这些容器并在这些元素满足特定条件后删除一些元素,如以下代码所示。
auto radiusMapCounter = m_markerRadiusMap.begin();
auto markerCounter = frames.markers.begin();
auto goodContoursCounter = mapGoodContours.begin();
if(m_markerRadiusMap.size()==ellipsePropertiesMap.size() && frames.markers.size()==ellipsePropertiesMap.size()
&& mapGoodContours.size()==ellipsePropertiesMap.size())
{
for(auto ellipsePropertyCounter = ellipsePropertiesMap.begin(); ellipsePropertyCounter != ellipsePropertiesMap.end(); ellipsePropertyCounter++)
{
float upperLimit = (float)m_upperFactor*(float)ellipsePropertyCounter->second.radius;
float lowerLimit = (float)m_lowerFactor*(float)ellipsePropertyCounter->second.radius;
if(ellipsePropertyCounter->second.minDistanceFromOtherEllipse>upperLimit
|| ellipsePropertyCounter->second.minDistanceFromOtherEllipse<lowerLimit)
{
ellipsePropertiesMap.erase(ellipsePropertyCounter);
m_markerRadiusMap.erase(radiusMapCounter);
frames.markers.erase(markerCounter);
mapGoodContours.erase(goodContoursCounter);
}
else
{
smallContours.push_back(goodContoursCounter->second);
}
radiusMapCounter++;
markerCounter++;
goodContoursCounter++;
}
}
我很困惑地发现有时我会出现如图所示的分割错误。 radiusMapCounter++;
我做错了什么?
从容器中删除迭代器指向的元素后,您无法递增迭代器。创建迭代器的副本,递增它,通过复制删除元素。
如果您使用的是 C++11 或更高版本,std::map::erase(...)
returns 最后一个删除元素之后的迭代器,因此您可以使用它而不是递增无效元素。在这种情况下,无需创建要在 erase
中使用的迭代器副本。
for(auto ellipsePropertyCounter = ellipsePropertiesMap.begin();
ellipsePropertyCounter != ellipsePropertiesMap.end();
/* do not increment iterator here */)
{
float upperLimit = (float)m_upperFactor*(float)ellipsePropertyCounter->second.radius;
float lowerLimit = (float)m_lowerFactor*(float)ellipsePropertyCounter->second.radius;
if(ellipsePropertyCounter->second.minDistanceFromOtherEllipse>upperLimit
|| ellipsePropertyCounter->second.minDistanceFromOtherEllipse<lowerLimit)
{
ellipsePropertyCounter = ellipsePropertiesMap.erase(ellipsePropertyCounter);
radiusMapCounter = m_markerRadiusMap.erase(radiusMapCounter);
markerCounter = frames.markers.erase(markerCounter);
goodContoursCounter = mapGoodContours.erase(goodContoursCounter);
}
else
{
smallContours.push_back(goodContoursCounter->second);
radiusMapCounter++;
markerCounter++;
goodContoursCounter++;
ellipsePropertyCounter++; // increment loop iterator only in 'else' case
}
}
这是一个 C++17 解决方案。
第一个 indexer
,它是 C++14,允许您在使用时无需特殊用途的助手即可内联创建和解压索引包:
template<class=void,std::size_t...Is>
void indexer( std::index_sequence<Is>... ) {
return [](auto&& f) {
using discard=int[];
(void)discard{0,((
f( std::integral_constant<std::size_t, Is>{} )
),void(),0)...};
};
}
template<std::size_t N>
void indexer() {
return indexer( std::make_index_sequence<N>{} );
}
现在,erase_if
,SFINAE 应将其限制为仅适用于关联(可能还有其他基于节点的)容器:
template<class Test, class...Maps>
bool erase_if( Test&& test, Maps&&...maps) {
using std::begin; using std::end;
std::tuple< decltype(begin(maps))... > its;
std::tuple< decltype(begin(maps))... > ends;
auto m_tup = std::tie(maps...);
auto index = indexer<sizeof...(maps)>;
index(
[&](auto i){
std::get<i>(its) = begin( std::get<i>(m_tup) );
std::get<i>(ends) = end( std::get<i>(m_tup) );
}
);
while (its != ends) {
auto deref_then_test = [&test](auto...its) {
return test( (*its)... );
};
if (std::apply(deref_then_test,its)) {
index(
[&](auto i){
std::get<i>(its) = std::get<i>(m_tup).erase(std::get<i>(its));
}
);
} else {
index(
[&](auto i){++std::get<i>(its);}
);
}
}
}
这让您可以根据对其中一个容器的测试从多个容器中擦除。
代码未经测试,可能包含拼写错误。