2012-02-13 53 views
7

在我当前的C++项目中,我有一个将整数键映射到对象的STL映射。算法返回一组条目。返回的数据依赖于算法的输入,因此无法预测:C++ STL:迭代器和reverse_iterator缺少基类的代码复制

class MyClass 
    { 
    //... 
    }; 

    int myAlgorithm(vector<int>::iterator inputIt) 
    { 
    // return a key for myMap which is calculated by the current value of inputData 
    } 

    int main(int argc, char *argv[]) 
    { 
    vector<int> inputData; 
    map<int, MyClass> myMap; 
    //<fill map with some data> 
    //<fill inputData> 

    vector<MyClass> result; 

    for (vector<int>::iterator it = inputData.begin(); it != inputData.end(); it++) 
    { 
     int myMapKey = myAlgorithm(*it); 
     // count() > 0 means "check whether element exists. Performance can be improved by replacing 
     // the operator[] and count() calls by map::find(). However, I want to simplify things 
     // in this example. 
     if (myMap.count(myMapKey) > 0) 
     { 
      // in some cases there is no entry in myMap 
      result.push_back(myMap[myMapKey]); 
     } 
    } 
    } 

如例子中提到的,我可以用查找替换map::count()operator[] -calls。 STL-reference表示map :: find()的复杂度是对数大小(O(log n))。

我发现在大多数情况下myMap中的条目对于结果中的两个连续条目非常接近。因此,我得出的结论,如果我取代了map.find(我会获得更好的性能)调用由迭代器:

 map<int, MyClass>::iterator myMapIt = myMap.begin(); 
    for (vector<int>::iterator it = inputData.begin(); it != inputData.end(); it++) 
    { 
     int myMapKey = myAlgorithm(*it); 
     // just increment iterator 
     while (myMapKey != myMapIt->first) 
     { 
      myMapIt++; 
      // we didn't find anything for the current input data 
      if (myMapIt == myMap::end() || myMapIt->first > myMapKey) 
      { 
       break; 
      } 
     } 

     // I know that I'm checking this twice, but that's not the point of my 
     // question ;) 
     if (myMapIt == myMap::end() || myMapIt->first > myMapKey) 
     { 
      // probably it would be better to move the iterator back to the position 
      // where we started searching, to improve performance for the next entry 
      myMapIt = myMap.begin(); 
     } 
     else 
     { 
      result.push_back(myMapIt.second); 
     } 
    } 

这个概念的作品,但我有一个很大的问题:根据inputData,我要向前或向后搜索。考虑我多次调用main()中的代码,并且inputData更改为这些调用。而不是检查是否增加或减少循环内的迭代器,我可以决定在输入for -loop之前。

我认为我很好只是切换到map<>::iteratormap<>::reverse_iterator使用rbegin()/rend(),而不是begin()/end()。但后来我意识到,reverse_iteratoriterator没有公共基类:

 map<int, MyClass>::base_iterator myIt; 
    if (/* ... */) 
    { 
     myMapIt = myMap::begin(); 
     myMapEndIt = myMap::end(); 
    } 
    else 
    { 
     myMapIt = myMap::rbegin(); 
     myMapEndIt = myMap::rend(); 
    } 
    /* for (...) ... */ 

这将是巨大的,但没有base_iterator

我知道这个问题的简单解决方法:我只需要复制整个for -loop,并将其调整为两种情况:

 if (/* ... */) 
    { 
     /* for(...) which uses normal iterator in the while-loop */ 
    } 
    else 
    { 
     /* for(...) which uses reverse iterator in the while-loop */ 
    } 

极坏的...你知道一个更好的解决方案?

+3

会调用一个函数模板的工作? – PlasmaHH 2012-02-13 14:23:55

+1

你是如何得出结论的,你会获得更好的表现?你有数据支持它吗?如果这不是应用程序中真正的瓶颈,那么您可能只是为自己做更多的工作。这就是说,这仍然是一个有趣的问题。 :) – Scott 2012-02-13 15:08:23

+0

由于使用'map :: find()'时O(log n)的复杂性,它无法假定下一个条目接近当前条目。此代码处于非常关键的位置,在几个嵌套循环内 – fishbone 2012-02-14 21:01:51

回答

6

当语言允许通用编程时,不需要公共基本类型。

你只需要意识到的是,你可以有几个嵌套的函数,其中每个选项导致不同的调用,而不是有一个长蜿蜒的线性函数,有几个选择。

以你的例子:

boost::any_iterator start, end; 
if (/* ... */) { 
    start = map.begin(), end = map.end(); 
} else { 
    start = map.rbegin(), end = map.rend(); 
} 

// do something with start and end 

您可以将代码为以下:

// Define a free-function in the .cpp to help factor common stuff 
template <typename FwdIt> 
static void dosomething(FwdIt start, FwdIt end) { 
    // do something with start and end 
} 

然后注入电话直入if/else体:

if (/* ... */) { 
    dosomething(map.begin(), map.end()); 
} else { 
    dosomething(map.rbegin(), map.rend()); 
} 

一件好事就是这样你可以减少函数中状态的变化次数,从而减少它们的复杂性。

+0

我试过,但我想知道为什么我的整个算法是5次现在慢点。 'dosomething'内联?我找不到任何理由为什么我的想法会导致更糟糕的表现 – fishbone 2012-02-15 13:37:17

+0

这只是我实现中的一个错误,现在它更快 – fishbone 2012-02-15 14:02:25

2

您可以使用模板:

template <typename T> 
void myFunction(T start, T end) 
{ 
    /* for (...) ... */ 
} 

map<int, MyClass>::base_iterator myIt; 
if (/* ... */) 
{ 
    myFunction(myMap.begin(), myMap.end()); 
} 
else 
{ 
    myFunction(myMap.rbegin(), myMap.rend()); 
} 
4

使用模板功能。就我所知(这是一个错误),标准库中继承用于模板的唯一地方是IOstream。

template<typename Iterator> ... stuff(Iterator begin, Iterator end) { 
    // implement loop here 
} 
if (/*...*/) { 
    stuff(map.rbegin(), map.rend()); 
} else { 
    stuff(map.begin(), map.end()); 
} 

不过,我的问题,如果你只会变得更好更改为始终O(1)的容器,就像一个unordered_map

+0

您是否有关于unordered_map的更多信息? 1.我无法想象它是如何工作的2.我读到它的复杂性不稳定,在最坏的情况下也可能是O(n) – fishbone 2012-02-15 13:34:58