c++x86-64arm64gcc8gcc11

Does gcc-8 on arm64 debian ignores friend operator<?


I want to compare two sets of objects. For the same code I get 2 different outputs on different machines. The code is compiled with two different compilers. On the x86-64 machine i used gcc-11, on the aarch64 (raspberry pi4) machine i used gcc-8. I have to use the gcc-8 on the raspberry because it's in the official repositories.

Has anyone an idea why this happens? Have I missed something?

#include <iostream>
#include <set>
#include <memory>

class ClassA {
private:
    ::std::string id;
public:
    explicit ClassA(::std::string id);

    [[nodiscard]] ::std::string getId() const;

    bool friend operator<(const ::std::shared_ptr<ClassA> &lhs, const ::std::shared_ptr<ClassA> &rhs);
};

ClassA::ClassA(::std::string id) : id{::std::move(id)} {}

::std::string ClassA::getId() const { return id; }

bool operator<(const ::std::shared_ptr<ClassA> &lhs, const ::std::shared_ptr<ClassA> &rhs) {
    auto r = (lhs->id < rhs->id);
    ::std::cout << ::std::boolalpha << "Comparing lhs->id " << lhs->id << ", with rhs->id " << rhs->id
                << ". The result is " << r << ::std::endl;
    return (lhs->id < rhs->id);
}

class ClassB {
public:
    ::std::set<::std::shared_ptr<ClassA>> members;

    void add(::std::shared_ptr<ClassA> a);
};

void ClassB::add(::std::shared_ptr<ClassA> a) {
    members.emplace(::std::move(a));
}

int main() {
    ::std::cout << "Create first set:" << ::std::endl;
    auto firstContainer = ::std::set<::std::shared_ptr<ClassA>>{::std::make_shared<ClassA>("_3"),
                                                                ::std::make_shared<ClassA>("_5")};
    ::std::cout << "Create second set:" << ::std::endl;
    auto secondContainer = ::std::set<::std::shared_ptr<ClassA>>{::std::make_shared<ClassA>("_5"),
                                                                 ::std::make_shared<ClassA>("_3")};
    auto b1 = ::std::make_shared<ClassB>();
    auto b2 = ::std::make_shared<ClassB>();
    ::std::cout << "Fill first ClassB instance:" << ::std::endl;
    for (const auto &r: firstContainer) {
        b1->add(r);
    }
    ::std::cout << "Fill second ClassB instance:" << ::std::endl;
    for (const auto &r: secondContainer) {
        b2->add(r);
    }
    auto result = ::std::equal(b1->members.begin(), b1->members.end(), b2->members.begin(),
                               [](const ::std::shared_ptr<ClassA> lhs, ::std::shared_ptr<ClassA> rhs) -> bool {
                                   return lhs->getId() == rhs->getId();
                               });
    ::std::cout << ::std::boolalpha << "The result is: " << result << ::std::endl;

    ::std::cout << "First ClassB members" << ::std::endl;
    for (const auto &r: b1->members) {
        ::std::cout << "Id " << r->getId() << ::std::endl;
    }

    ::std::cout << "Second ClassB members" << ::std::endl;
    for (const auto &r: b2->members) {
        ::std::cout << "Id " << r->getId() << ::std::endl;
    }
    return 0;
}

The x86-64 output.

Create first set:
Comparing lhs->id _3, with rhs->id _5. The result is true
Comparing lhs->id _5, with rhs->id _3. The result is false
Create second set:
Comparing lhs->id _5, with rhs->id _3. The result is false
Comparing lhs->id _3, with rhs->id _5. The result is true
Comparing lhs->id _3, with rhs->id _5. The result is true
Fill first ClassB instance:
Comparing lhs->id _5, with rhs->id _3. The result is false
Comparing lhs->id _3, with rhs->id _5. The result is true
Comparing lhs->id _5, with rhs->id _3. The result is false
Fill second ClassB instance:
Comparing lhs->id _5, with rhs->id _3. The result is false
Comparing lhs->id _3, with rhs->id _5. The result is true
Comparing lhs->id _5, with rhs->id _3. The result is false
The result is: true
First ClassB members
Id _3
Id _5
Second ClassB members
Id _3
Id _5

The aarch64 output:

Create first set:
Create second set:
Fill first ClassB instance:
Fill second ClassB instance:
The result is: false
First ClassB members
Id _3
Id _5
Second ClassB members
Id _5
Id _3

Solution

  • The problem is here:

    bool operator<(const ::std::shared_ptr<ClassA> &lhs, const ::std::shared_ptr<ClassA> &rhs)
    

    The shared pointer has its own comparison operators which compares stored pointers. There is also owner_less which compares owned pointers (a specially constructed shared pointer can own one object but point to another, e.g. it can point to a member of the owned object).

    If you need to compare the pointed-to objects, you should write a comparator doing just that, and pass it to the set as second template argument. Like:

    struct my_ClassA_id_less {
        bool operator() (const ::std::shared_ptr<ClassA> &lhs, const ::std::shared_ptr<ClassA> &rhs) {
            // your comparison code here, as for operator<
        }
    };
    
    ::std::set<std::shared_ptr<ClassA>, my_ClassA_id_less> my_set;