使用struct作为键在map中输入值

时间:2011-11-28 07:47:31

标签: c++ map struct key

我有一个结构作为键的地图,我已经重载了<运算符,但是地图将每个条目存储为单独的键,即使它们是相同的。代码如下:

#include <iostream>
#include <vector>
#include <map>

using namespace std;

struct vertex 
{
    int color;
    vertex *pi;
    int index;

    bool operator<(const vertex & v ) const {
        return this->index < v.index;
    }
    bool operator==(const vertex & v) const {
        return this->index == v.index;
    }
};


int main()
{
    int x, y, num_edges;
    vector<vertex* > v;
    vertex *temp1, *temp2, *temp;
    map<vertex*, vector<vertex*> > m;
    map<vertex*, vector<vertex*> >::iterator it;

    cout << "\nEnter no. of edges: ";
    cin >> num_edges;

    for( int i = 0; i < num_edges; i++ )
    {
        cout << "\nEnter source: ";
        cin >> x;
        cout << "\nEnter dest: ";
        cin >> y;

        temp1 = new vertex;
        temp2 = new vertex;
        temp1->index = x;
        temp2->index = y;

        m[temp1].push_back(temp2);
        m[temp2].push_back(temp1);
    }

    temp1 = new vertex;
    temp2 = new vertex;

    cout << "\nPrinting map: " << endl;
    for( it = m.begin(); it != m.end(); it++ )
    {
        temp = (*it).first;

        cout << temp->index << "\t";

        v = (*it).second;
        for( int i = 0; i < v.size(); i++ )
        {
            temp1 = v[i];
            cout << temp1->index << "\t";
        }
        cout << "\n";
        v.clear();
    }
    for( it = m.begin(); it != m.end(); it++ )
    {
        temp = (*it).first;
        v.push_back(temp);
    }
    return 0;
}

我现在得到的输出是:

Enter no. of edges: 4

Enter source: 1

Enter dest: 3

Enter source: 4

Enter dest: 3

Enter source: 4

Enter dest: 2

Enter source: 2

Enter dest: 1

Printing map: 
1   3   
3   1   
4   3   
3   4   
4   2   
2   4   
2   1   
1   2   

但它应该是:

1 3 2

2 4 1

3 1 4

4 3 2

我哪里错了?

4 个答案:

答案 0 :(得分:2)

std :: map会将你给它的类型作为键(vertex*)进行比较,但你在顶点定义<运算符。

您可以将结构本身用作键,或者 - 如果必须使用指针 - 您必须为地图提供比较指针的方法。

现在,std::map使用std::less作为比较谓词,在<的therm中定义(这就是为什么使用struct本身,你可以通过重载来实现结果{{ 1}})。

你可以:

o)定义一个比较顶点*的谓词:它可以是

<

然后将地图定义为

template <class T> //assume T is a pointer or pointer-like class
struct redirected_less : public std::binary_function <T,T,bool> 
{
    bool operator() (const T& x, const T& y) const {return *x < *y;}
};

o)将std :: less for vertex *专门化为

std::map<vertex*, vector<vertex*>,  redirected_less<vertex*> >

并将您的地图声明为

namespace std
{
     template <> 
     struct less<vertex*> : binary_function <vertex*,vertex*,bool> 
     {
         bool operator() (vertex* x, vertex* y) const {return *x < *y; }
     };
}
像往常一样。

我个人更喜欢第一个(提供更本地化的代码,在未来的读数中减少“奥术”)

答案 1 :(得分:1)

您不能将指针用作键。如果你有结构,根据你的规则是“相同的”,但是用new分配堆,那么它们的指针将永远不会相同。

使用结构而不是指针作为关键。

答案 2 :(得分:1)

map<vertex*, vector<vertex*> > m;// its key type vertex*  

m使用

bool operator < (vertex * const, vertex* const) ;
订购时

所以你需要重载

bool operator < (vertex * const, vertex* const); 

我们这里有一个问题。我们不能重载指针。我们可以像这样提供我们自己的比较函数:

struct cmp{ 
      bool operator ()(vertex * const first, vertex* const second)
      { return first.index < second->index;} 
 };
cmp _cmp;
map<vertex*, vector<vertex*>, cmp> m(_cmp);

答案 3 :(得分:0)

C ++ 11

Emilio Garavaglia analyzed the problem with your code and how to fix it很好。但是,如果可以使用C++11功能,则可以稍微现代化解决方案。例如,您可以使用lambda expression而不是为结构定义operator<,并将其传递给地图的构造函数。如果您无法修改要存储在地图中的结构,或者要为不同的地图提供不同的比较功能,则此方法很有用。 Range-based for loops使您不必处理迭代器,而占位符auto使您不必指定冗长的容器元素类型。我还要提及的是,您无需指定operator==即可使用地图。

vertex为键的解决方案

为清楚起见,我将用户输入替换为硬编码值:

struct vertex {
    int color;
    vertex *pi;
    int index;
};

int main() {
    auto comp = [](const vertex& v1, const vertex& v2) { return v1.index < v2.index; };
    std::map<vertex, std::vector<vertex>, decltype(comp)> m(comp);

    // Replace user input.
    std::vector<std::pair<int, int>> edges = { {1, 3}, {4, 3}, {4, 2}, {2, 1} };

    // Fill the map.
    for(auto const &e : edges) {
        vertex temp1;
        vertex temp2;
        temp1.index = e.first;
        temp2.index = e.second;
        m[temp1].push_back(temp2);
        m[temp2].push_back(temp1);
    }

    // Print the map.
    std::cout << "Printing map:" << std::endl;
    for (auto const &kv : m) {
        std::cout << kv.first.index << " =>";
        for (auto const &v : kv.second)
            std::cout << " " << v.index;
        std::cout << std::endl;
    }

    return 0;
}

输出:

打印地图:
1 => 3 2
2 => 4 1
3 => 1 4
4 => 3 2

注意:如果可以使用C++17,则可以进一步使用structured binding缩短用于打印地图的代码,如here on Coliru所示。 / p>

vertex*为键的解决方案

基本上,您必须将以上代码中所有出现的vertex替换为vertex*。另外,您必须确保lambda表达式不比较给定的指针,而是比较指针所引用的结构的内容:

auto comp = [](const vertex* pv1, const vertex* pv2) { return pv1->index < pv2->index; };
std::map<vertex*, std::vector<vertex*>, decltype(comp)> m(comp);

还必须确保最后删除所有动态分配的顶点。例如,您可以通过以下方式正确清空地图:

for (auto const &kv : m)
    for (auto const pv : kv.second)
        delete pv;
m.clear();

Code on Coliru

注意:为了简化内存管理,您应该更喜欢在地图中存储smart pointers,例如std::unique_ptrstd::shared_ptr