Search code examples
c++c++17operator-overloadingnullptr

user defined pointer wrapper and nullptr comparison does not call the operator I have provided


I want to compare if my my_ptr object is nullptr or not. I expected x == nullptr to call the following operator I have provided.

operator==(std::nullptr_t, const my_ptr<V>& r)

but it is not getting called. I don't see " == nullptr called" displayed on my output screen. And also if I uncomment the following line

my_ptr<int> x2{p2}

the result flips. I checked this behaviour in g++ and some online compilers

#include <iostream>
#include <type_traits>
#include <utility>

template <typename T>
class my_ptr
{
    T* t = nullptr;

  public:
    my_ptr() 
    {
        std::cout<<"\n No arguments constructor called\n";
    }

    my_ptr(std::nullptr_t) 
    {
        std::cout<<"\n nullptr constructor called\n";
    }

    my_ptr(T* t_)
        : t(t_)
    {
        std::cout<<"\n pointer constructor called\n";
    }
  
    operator const void*() const 
    {
        std::cout<<"\n void param called ";
        return t; 
    }

};

template <typename U, typename V>
bool operator==(const my_ptr<U>& l, const my_ptr<V>& r)
{
    std::cout<<"\n == called\n";
    return l.get() == r.get();
}

template <typename U, typename V>
bool operator==(std::nullptr_t, const my_ptr<V>& r)
{
    std::cout<<"\n == nullptr called\n";
    return false;
}


int main()
{
    int *p;
    my_ptr<int> x{p};
   
    int *p2;
    my_ptr<int> x2{p2}; //uncommenting this line changes the result value
 
    std::cout<<"\n "<<int(x == nullptr)<<"\n";

    return 0;
}

Output seen

  1. with my_ptr x2{p2}; commented

pointer constructor called

void param called 0

  1. with my_ptr x2{p2}; un-commented

pointer constructor called

pointer constructor called

void param called 1


Solution

  • There are two issues with your operator.

    template <typename U, typename V>
    bool operator==(std::nullptr_t, const my_ptr<V>& r)
    {
        std::cout<<"\n == nullptr called\n";
        return false;
    }
    

    It has a template argument U that cannot be deduced from the function parameters, hence you cannot use it with operator call syntax x == y.

    Second, you defined operator==(std::nullptr_t,const my_ptr<V>&) but you call operator==(const my_ptr<V>&,std::nullptr_t). Before C++20 you need to explicitly implement the other order, also when the operator is symmetric.

    You get expected output with this operator==:

    template <typename V>
    bool operator==(const my_ptr<V>&, std::nullptr_t)
    {
        std::cout<<"\n == nullptr called\n";
        return false;
    }
    

    Live Demo

    As pointed out by DaveS, since C++20, implementing operator(std::nullptr,cosnt my_ptr<V>&) is sufficient to call x == nullptr.

    Last but not least, your code has undefined behavior due to using p and p1 when they are uninitialized.