【问题标题】:How can I enter values into a map with a struct as key?如何将值输入到以结构为键的映射中?
【发布时间】:2012-01-07 17:33:21
【问题描述】:

我有一个以struct 作为键的映射,我重载了< 运算符,但映射将每个条目存储为单独的键,即使它们相同。代码如下:

#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

我哪里错了?

【问题讨论】:

    标签: c++ dictionary struct key stdmap


    【解决方案1】:

    C++11

    Emilio Garavaglia analyzed the problem with your code and how to fix it 很好。但是,如果您可以使用C++11 功能,那么您可以稍微现代化您的解决方案。例如,您可以使用lambda expression 而不是为您的结构定义operator&lt;,并将其传递给地图的构造函数。如果您无法修改要存储在地图中的结构,或者如果您想为不同的地图提供不同的比较函数,则此方法很有用。 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

    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

    【讨论】:

      【解决方案2】:

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

      使用结构而不是它们的指针作为键。

      【讨论】:

        【解决方案3】:

        std::map 将比较您给它的类型作为键 (vertex*),但您在顶点上定义 &lt; 运算符。

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

        现在,std::map 使用std::less 作为比较谓词,它在&lt; 的therm 中定义(这就是为什么使用结构本身,您可以通过重载&lt; 来实现结果)。

        您可以:

        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;}
        };
        

        然后将你的地图定义为

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

        o) 将 vertex* 的 std::less 特化为

        namespace std
        {
             template <> 
             struct less<vertex*> : binary_function <vertex*,vertex*,bool> 
             {
                 bool operator() (vertex* x, vertex* y) const {return *x < *y; }
             };
        }
        

        并将您的地图声明为

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

        像往常一样。

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

        【讨论】:

        • +1,即使我会提供两个redirected_less,一个采用T 并实现operator()(T const &amp;, T const&amp;) 没有取消引用,以及一个专业化采用T* 并实现operator()( T const*, T const* ) 取消引用,与定义 remove_constremove_reference 元函数的方式相同——即如果将key 从值更改为指针类型,则用户代码无需更改。
        • @DavidRodríguez-dribeas:我明白你的意思,但我在看一个更统一的声明 'std::map, redirected_less >' 尊重to 'std::map, redirected_less >' 但这更像是一个鸡和蛋的问题......可能对上下文的更高看法可以更倾向于其中一个...
        • @EmilioGaravaglia:非常感谢!早些时候,我用 struct 替换了指向 struct 的指针,但是按照你的方式,我现在可以运行我的原始代码了!再次感谢!
        【解决方案4】:
        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);
        

        【讨论】:

        • @fefe 算了。我们可以定义一个等效的函数或仿函数并将其作为映射的模板参数传递
        猜你喜欢
        • 2018-07-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多