Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparator operator in weak_ptr C++

I am still a novice in the new stl members.Can anyone point out why this code is giving segmentation fault?

#include<memory>
#include<stdio.h>
#include<map>
#include<set>
#include<string>
using namespace std;
struct StubClass
{
    weak_ptr<string> b;
    int c;
    friend bool operator==(StubClass x,StubClass y);
    friend bool operator<(StubClass x,StubClass y);
    StubClass(weak_ptr<string> x):b(x){c=5;}    
};
bool operator==(StubClass d,StubClass c) { return d.b==c.b;}
bool operator<(StubClass d,StubClass c) { return d.b<c.b; }


int main()
{
    shared_ptr<string> spPtr(new string("Hello"));
    weak_ptr<string> wpPtr(spPtr);
    StubClass hello(wpPtr);
    set<StubClass> helloSet;
    helloSet.insert(hello);
    if(helloSet.find(StubClass(wpPtr))!=helloSet.end()) printf("YAYA");
    else puts("Bye");
}

The error is in line

if(helloSet.find(StubClass(wpPtr))!=helloSet.end()) printf("YAYA");

More research reveals there is a problem when the StubClass's comparator function is called. I am compiling the program here

EDIT:

bool operator==(StubClass d,StubClass c) { return d.b.lock()==c.b.lock();}
bool operator<(StubClass d,StubClass c) { return d.b.lock()<c.b.lock(); }

This resolved the issue.I should be reading more.:( Anyways can anyone from the community explain the reason why the first code gives SIGSEGV.I figured it out eventually,but still a nice explanation won't hurt. :)

like image 909
bashrc Avatar asked Jan 16 '23 10:01

bashrc


2 Answers

Your original code segfaults because you've accidentally set up an infinite recursion:

bool operator<(StubClass d,StubClass c) { return d.b<c.b; }

There is no operator< for weak_ptr. However you do have an implicit conversion from weak_ptr to StubClass. And StubClass has an operator<. So this function calls itself indefinitely: thus the segfault.

The currently accepted answer from inkooboo will also lead to undefined behavior, probably resulting in a crash. As weak_ptrs become expired during the execution of your program (something more involved than your test case), then the ordering of them will change. When this happens between two weak_ptrs in the set, the set will become corrupted, likely leading to a crash. However there is a way around this using owner_less which was designed specifically for this use case:

bool operator==(const StubClass& d, const StubClass& c)
{
    return !owner_less<weak_ptr<string>>()(d.b, c.b) &&
           !owner_less<weak_ptr<string>>()(c.b, d.b);
}
bool operator<(const StubClass& d, const StubClass& c)
{
    return owner_less<weak_ptr<string>>()(d.b, c.b);
}

Or, if you prefer, this can also be coded using the member function owner_before. Both are equivalent:

bool operator==(const StubClass& d, const StubClass& c)
{
    return !d.b.owner_before(c.b) && !c.b.owner_before(d.b);
}
bool operator<(const StubClass& d, const StubClass& c)
{
    return d.b.owner_before(c.b);
}

Using these functions, even when one weak_ptr expires and the other doesn't, their ordering remains stable. And thus you'll have a well-defined set.

like image 53
Howard Hinnant Avatar answered Jan 18 '23 22:01

Howard Hinnant


If you want to compare strings stored in weak_ptr do this:

bool operator<(StubClass d, StubClass c) 
{
    std::shared_ptr<std::string> a = d.b.lock();
    std::shared_ptr<std::string> b = c.b.lock();

    if (!a && !b)
        return false; 

    if (!a)
        return true;

    if (!b)
        return false;

    return *a < *b;
}

Run result

like image 32
inkooboo Avatar answered Jan 18 '23 23:01

inkooboo