【问题标题】:Can't find structs in a C++ set在 C++ 集中找不到结构
【发布时间】:2020-12-16 17:26:38
【问题描述】:

我正在编写一个包含一些 3d 图形程序结构的集合。我正在尝试做的一件事是找出其中一个结构是否在一组中以使用该知识进行操作。这是一个mwe:

#include <iostream>
#include <set>

class Malla3D
{
public:
    int id;
};

struct Objeto
{
    Malla3D * modelo;
    bool operator  < (const Objeto & otro) const;
    bool operator == (const Objeto & otro) const;
};

bool Objeto :: operator < (const Objeto & otro) const
{
    return this < &otro;
}

bool Objeto :: operator == (const Objeto & otro) const
{
    return modelo == otro.modelo;
}

int main ()
{
    std::set<Objeto> objetos;

    Malla3D * malla1 = new Malla3D({1});
    Malla3D * malla2 = new Malla3D({2});
    Malla3D * malla3 = new Malla3D({3});
    Malla3D * malla4 = new Malla3D({4});

    Objeto objeto1({malla1});
    Objeto objeto2({malla2});
    Objeto objeto3({malla3});
    Objeto objeto4({malla4});

    objetos.insert(objeto1);
    objetos.insert(objeto2);
    objetos.insert(objeto3);
    objetos.insert(objeto4);

    for (auto it = objetos.cbegin(); it != objetos.cend(); ++it)
        std::cout << "Item in set: " << (*it).modelo->id << std::endl;

    if (objetos.find(objeto1) != objetos.cend())
        std::cout << "Found 1." << std::endl;
    else
        std::cout << "Couldn't find 1." << std::endl;

    if (objetos.find(objeto2) != objetos.cend())
        std::cout << "Found 2." << std::endl;
    else
        std::cout << "Couldn't find 2." << std::endl;

    if (objetos.find(objeto3) != objetos.cend())
        std::cout << "Found 3." << std::endl;
    else
        std::cout << "Couldn't find 3." << std::endl;

    if (objetos.find(objeto4) != objetos.cend())
        std::cout << "Found 4." << std::endl;
    else
        std::cout << "Couldn't find 4." << std::endl;

    delete malla1;
    delete malla2;
    delete malla3;
    delete malla4;
}
~
➜ g++ -g -Wall -Wextra -Wpedantic -std=c++17 mwe.cpp

~ 
➜ ./a.out
Item in set: 1
Item in set: 2
Item in set: 3
Item in set: 4
Couldn't find 1.
Couldn't find 2.
Couldn't find 3.
Couldn't find 4.

因此,基本上,如果两个Objetos 指向相同的内存地址,则它们的modelos 是相同的。这样,我可以从 Malla3D 类中删除 id 成员数据。我已经用 gdb 测试了operator ==,它按预期工作,所以我不明白为什么find 除了end() 之外不能返回。我做错了什么?

【问题讨论】:

  • 集合成员的等价性由其排序关系决定——默认为operator&lt;——而不是==
  • @molbdnilo 我不明白那是怎么回事。如何知道一个元素是否等于集合中的一个?
  • std::set 是有序的,两个元素 ab 当且仅当 !(a &lt; b) &amp;&amp; !(b &lt; a) 是等价的——也就是说,如果两者都不在另一个之前。 (等价是比等价更弱的关系。)

标签: c++ iterator set find c++17


【解决方案1】:

您的运算符正在比较对象指针,而不是成员值。当您将insert() 一个Objeto 对象放入std::set 时,将存储一个副本。当您稍后尝试 find() Objeto 对象时,operator&lt; 正在比较的 this 指针将不是您所期望的(std::set 不使用 operator== 进行比较) ,因此为什么找不到匹配项。

试试这个:

#include <iostream>
#include <set>

struct Malla3D
{
    int id;

    bool operator  < (const Malla3D & otro) const;
    bool operator == (const Malla3D & otro) const;
};

bool Malla3D :: operator < (const Malla3D & otro) const
{
    return id < otro.id;
}

bool Malla3D :: operator == (const Malla3D & otro) const
{
    return id == otro.id;
}

struct Objeto
{
    Malla3D modelo;
    bool operator  < (const Objeto & otro) const;
    bool operator == (const Objeto & otro) const;
};

bool Objeto :: operator < (const Objeto & otro) const
{
    return modelo < otro.modelo;
}

bool Objeto :: operator == (const Objeto & otro) const
{
    return modelo == otro.modelo;
}

int main ()
{
    std::set<Objeto> objetos;

    Objeto objeto1{1};
    Objeto objeto2{2};
    Objeto objeto3{3};
    Objeto objeto4{4};

    objetos.insert(objeto1);
    objetos.insert(objeto2);
    objetos.insert(objeto3);
    objetos.insert(objeto4);

    for (auto it = objetos.cbegin(); it != objetos.cend(); ++it)
        std::cout << "Item in set: " << it->modelo.id << std::endl;

    if (objetos.find(objeto1) != objetos.cend())
        std::cout << "Found 1." << std::endl;
    else
        std::cout << "Couldn't find 1." << std::endl;

    if (objetos.find(objeto2) != objetos.cend())
        std::cout << "Found 2." << std::endl;
    else
        std::cout << "Couldn't find 2." << std::endl;

    if (objetos.find(objeto3) != objetos.cend())
        std::cout << "Found 3." << std::endl;
    else
        std::cout << "Couldn't find 3." << std::endl;

    if (objetos.find(objeto4) != objetos.cend())
        std::cout << "Found 4." << std::endl;
    else
        std::cout << "Couldn't find 4." << std::endl;
}

Demo

或者,如果你真的需要在Objeto 中使用Malla3D* 指针(为什么?):

#include <iostream>
#include <set>

struct Malla3D
{
    int id;

    bool operator  < (const Malla3D & otro) const;
    bool operator == (const Malla3D & otro) const;
};

bool Malla3D :: operator < (const Malla3D & otro) const
{
    return id < otro.id;
}

bool Malla3D :: operator == (const Malla3D & otro) const
{
    return id == otro.id;
}

struct Objeto
{
    Malla3D* modelo;
    bool operator  < (const Objeto & otro) const;
    bool operator == (const Objeto & otro) const;
};

bool Objeto :: operator < (const Objeto & otro) const
{
    return *modelo < *(otro.modelo);
}

bool Objeto :: operator == (const Objeto & otro) const
{
    return *modelo == *(otro.modelo);
}

int main ()
{
    std::set<Objeto> objetos;

    Malla3D * malla1 = new Malla3D{1};
    Malla3D * malla2 = new Malla3D{2};
    Malla3D * malla3 = new Malla3D{3};
    Malla3D * malla4 = new Malla3D{4};
    
    Objeto objeto1{malla1};
    Objeto objeto2{malla2};
    Objeto objeto3{malla3};
    Objeto objeto4{malla4};

    objetos.insert(objeto1);
    objetos.insert(objeto2);
    objetos.insert(objeto3);
    objetos.insert(objeto4);

    for (auto it = objetos.cbegin(); it != objetos.cend(); ++it)
        std::cout << "Item in set: " << it->modelo->id << std::endl;

    if (objetos.find(objeto1) != objetos.cend())
        std::cout << "Found 1." << std::endl;
    else
        std::cout << "Couldn't find 1." << std::endl;

    if (objetos.find(objeto2) != objetos.cend())
        std::cout << "Found 2." << std::endl;
    else
        std::cout << "Couldn't find 2." << std::endl;

    if (objetos.find(objeto3) != objetos.cend())
        std::cout << "Found 3." << std::endl;
    else
        std::cout << "Couldn't find 3." << std::endl;

    if (objetos.find(objeto4) != objetos.cend())
        std::cout << "Found 4." << std::endl;
    else
        std::cout << "Couldn't find 4." << std::endl;

    delete malla1;
    delete malla2;
    delete malla3;
    delete malla4;
}

Demo

【讨论】:

  • 抱歉回复晚了。据我了解,集合中复制的结构是最高级别的,对吗?它的副本应该引用相同的内存地址,因此 ==ing 指针应该返回 true。我知道可以使用 id 属性来完成,但我想尝试仅引用内存地址。我还需要使用指针,因为结构是多态的,所以这就是为什么我试图使用它并首先摆脱 id (我没有在操作中指定它,抱歉造成混淆)。
  • 我还把 Malla3D 改成了一个类。 mwe是一样的,但我一团糟。再次为这个写得不好的问题感到抱歉:/
  • 在集合中复制的结构是最高级别的,对吧?” - 我不确定我理解你在说什么。 “它的副本应该引用相同的内存地址,所以 ==ing 指针应该返回 true” - 是的,但正如我所说,std::set 不使用 operator== 来比较元素,它使用 @ 987654338@ 相反,在该运算符中,this 指向副本,而不是原件。 “我还需要使用指针,因为结构是多态的” - 然后我建议你定义一个通过多态执行对象比较的虚拟方法,根本不比较原始指针
  • "我还将 Malla3D 更改为一个类" - 在这种情况下没有区别。 structclass 被视为相同,它们之间的唯一区别是class 默认使用private 可见性,而struct 使用public
【解决方案2】:

正如 molbdnilo 所说,我只需要使用 operator &lt; 即可让程序正常运行。

解决办法:去掉operator ==,把另一个改写成下面这样:

bool Objeto :: operator < (const Objeto & otro) const
{
    return modelo < otro.modelo;
}

这样,内存地址是有序的,可以匹配find。对于一个非常愚蠢的问题,这是一个不令人满意的解决方案,但感谢 Remy,我学到了很多东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-18
    • 2020-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    • 1970-01-01
    相关资源
    最近更新 更多