Use lower_bound() before iterating multimap entries of a key (#1776)

Several places look up a multimap with find(key) and then iterate while
iter->first == key, assuming find() returns the first element with that key.
The standard does not guarantee this, and recent libc++ (Apple clang 21 /
libc++ 2200) returns an arbitrary matching element. graph::findLink() then
misses existing links and Optimizer::getConnectedGraph() aborts with
"Condition (kter!=linksIn.end()) not met!" on graphs with loop closures or
multiple sessions (rtabmap-export --opt 0, rtabmap-reprocess, etc.).

Replace find() with lower_bound() at those sites and add a regression test.

Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Co-authored-by: matlabbe <[email protected]>
This commit is contained in:
Nathan Totten
2026-09-27 18:37:17 -07:00
committed by GitHub
co-authored by Claude Opus 5.5 matlabbe
parent 8035be52ff
commit 5c9cfa98fe
6 changed files with 46 additions and 23 deletions
+5 -5
View File
@@ -2029,7 +2029,7 @@ void DatabaseViewer::updateIds()
envSensors_.insert(std::make_pair(ids_[i], sensors));
if(w>=0)
{
for(std::multimap<int, Link>::iterator iter=links.find(ids_[i]); iter!=links.end() && iter->first==ids_[i]; ++iter)
for(std::multimap<int, Link>::iterator iter=links.lower_bound(ids_[i]); iter!=links.end() && iter->first==ids_[i]; ++iter)
{
// Make compatible with old databases, when "weight=-1" was not yet introduced to identify ignored nodes
if(iter->second.type() == Link::kNeighbor || iter->second.type() == Link::kNeighborMerged)
@@ -2070,7 +2070,7 @@ void DatabaseViewer::updateIds()
previousPose=p;
//links
for(std::multimap<int, Link>::iterator jter=links.find(ids_[i]); jter!=links.end() && jter->first == ids_[i]; ++jter)
for(std::multimap<int, Link>::iterator jter=links.lower_bound(ids_[i]); jter!=links.end() && jter->first == ids_[i]; ++jter)
{
if(jter->second.type() == Link::kNeighborMerged)
{
@@ -4852,7 +4852,7 @@ void DatabaseViewer::updateCovariances(const QList<Link> & links)
infMatrix.clone(),
currentLink.userDataCompressed());
bool updated = false;
std::multimap<int, Link>::iterator iter = linksRefined_.find(currentLink.from());
std::multimap<int, Link>::iterator iter = linksRefined_.lower_bound(currentLink.from());
while(iter != linksRefined_.end() && iter->first == currentLink.from())
{
if(iter->second.to() == currentLink.to() &&
@@ -6681,7 +6681,7 @@ void DatabaseViewer::editConstraint()
{
cv::Mat covariance = dialog.getCovariance();
Link newLink(link.from(), link.to(), link.type(), dialog.getTransform(), covariance.inv());
std::multimap<int, Link>::iterator iter = linksRefined_.find(link.from());
std::multimap<int, Link>::iterator iter = linksRefined_.lower_bound(link.from());
while(iter != linksRefined_.end() && iter->first == link.from())
{
if(iter->second.to() == link.to() &&
@@ -9462,7 +9462,7 @@ void DatabaseViewer::refineConstraint(int from, int to, Registration * reg, Regi
Link newLink(currentLink.from(), currentLink.to(), currentLink.type(), transform, info.covariance.inv(), currentLink.userDataCompressed());
bool updated = false;
std::multimap<int, Link>::iterator iter = linksRefined_.find(currentLink.from());
std::multimap<int, Link>::iterator iter = linksRefined_.lower_bound(currentLink.from());
while(iter != linksRefined_.end() && iter->first == currentLink.from())
{
if(iter->second.to() == currentLink.to() &&