简体   繁体   中英

Can't find structs in a C++ set

I'm writing a set that contains some structs for a 3d graphics program. One of the things I'm trying to do is find if one of the structs is in a set to operate with that knowledge. Here's a 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.

So, basically, two Objeto s are the same if their modelo s point to the same memory address. That way, I can ditch the id member data from the Malla3D class. I've tested the operator == with gdb and it works as intended, so I don't understand why find can't return other than end() . What am I doing wrong?

Your operators are comparing object pointers, not member values. When you insert() an Objeto object into the std::set , a copy is stored. When you then try to find() a Objeto object later, the this pointers that are being compared by your operator< won't be what you are expecting ( std::set does not use operator== for comparisons), hence why matches are not found.

Try this instead:

#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

Or, if you really need to use Malla3D* pointers in Objeto (why?):

#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

As molbdnilo said, I just need to use the operator < for the program to work correctly.

The solution: remove the operator == and rewrite the other to the following:

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

This way, the memory addresses are ordered and can be matched with find . It's an unsatisfying solution to a very dumb problem, but I learnt a lot thanks to Remy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM