Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Less-than function dereferencing pointers

Tags:

c++

There are cases where one has pointers in an STL container and where less-than comparison shall not be made by pointer but by the objects pointed to. A simple example would be a vector which shall be sorted by the real numbers. Currently I solve this with:

template<class T_PTR> struct ltDeref
{
    bool operator()(T_PTR p0,T_PTR p1) const {return *p0<*p1;}
};

and use it as

vector<double*> vIn;
sort(vIn.begin(),vIn.end(),ltDeref<double*>());

or

set<double*,ltDeref<double*> > someSet;

Instead of writing my own comparison function, is there a more "standard" way in C++ which doesn't require a user made template?

like image 632
Raffi Avatar asked Nov 13 '22 14:11

Raffi


1 Answers

Often you can use the functors available in functional to construct a resulting sort functor purely from standard constructions.

But there is none to dereference a pointer T* so, you'll have to use your own comparator.


The closest you can get is when your "pointer type" is not a primitive, but some User-Defined-Type with an operator* that can be addressed.

The following code is C++11 (for the use of std::bind which is simpler than std::bind1st and std::bind2nd).

#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>

// Fakes a "smart pointer" wrapper around data
template <typename T>
struct Ptr
{
    Ptr(T data) : data(data) {};
    const T& operator*() const { return data; }

private:
    T data;
};

int main()
{
    std::vector<Ptr<double>> vIn;
    vIn.push_back(Ptr<double>(5));
    vIn.push_back(Ptr<double>(2));
    vIn.push_back(Ptr<double>(6));

    using namespace std::placeholders;
    std::sort(
        vIn.begin(),
        vIn.end(),
        std::bind(
            std::less<double>(),
            std::bind(&Ptr<double>::operator*, _1),
            std::bind(&Ptr<double>::operator*, _2)
        )
    );

    std::vector<Ptr<double>>::const_iterator it = vIn.begin(), end = vIn.end();
    for ( ; it != end; ++it)
        std::cout << ',' << **it;
}
  • Output: ,2,5,6

As such, if instead of double* you have std::unique_ptr<double> or std::shared_ptr<double>, then this could work:

#include <vector>
#include <memory>
#include <algorithm>
#include <functional>
#include <iostream>

int main()
{
    typedef std::unique_ptr<double> STDUPD;

    std::vector<STDUPD> vIn;
    vIn.push_back(STDUPD(new double(5)));
    vIn.push_back(STDUPD(new double(2)));
    vIn.push_back(STDUPD(new double(6)));

    using namespace std::placeholders;
    std::sort(
        vIn.begin(),
        vIn.end(),
        std::bind(
            std::less<double>(),
            std::bind(&STDUPD::operator*, _1),
            std::bind(&STDUPD::operator*, _2)
        )
    );

    std::vector<STDUPD>::const_iterator it = vIn.begin(), end = vIn.end();
    for ( ; it != end; ++it)
        std::cout << ',' << **it;
}
  • Same output

Yet another reason to avoid "raw" pointers if you can...

like image 184
Lightness Races in Orbit Avatar answered Nov 15 '22 06:11

Lightness Races in Orbit