6

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?

default
  • 11,485
  • 9
  • 66
  • 102
Raffi
  • 107
  • 1
  • 8
  • 3
    why set` in set instead of just `set` ? – billz Dec 30 '12 at 10:46
  • 3
    There are cases where one has **pointers to pointers** in an STL container. Shouldn't there be a standard solution in C++ which doesn't require a user made template? There are cases where one has [**pointers to pointers to pointers**](http://www.c2.com/cgi/wiki?ThreeStarProgrammer) ... – Praetorian Dec 30 '12 at 10:53
  • @billz This is a simple example to reduce the question to the minimum. You can replace double* by a pointer to some fat object that was created on the heap. – Raffi Dec 30 '12 at 11:01
  • How should STL know how to implement compare operation from UDT? – billz Dec 30 '12 at 11:06
  • 1
    @Praetorian Sure, but pointers in a container are an every-day thing while pointers to pointers are in most cases just a habit of people who programmed plain C before. Therefore I assumed that there is some feature in the language to dereference the first layer. – Raffi Dec 30 '12 at 11:11
  • Currently your question is answered with `yes/no`. What is the actual problem that you are trying to solve here? – default Dec 30 '12 at 11:11
  • @billz It would use operator<() of the dereferenced object. – Raffi Dec 30 '12 at 11:12
  • @Default If there is no standard solution, its fine. But if there is one, my template would re-invent the wheel which would not be a good programming style. – Raffi Dec 30 '12 at 11:14
  • I took the liberty to change the phrasing of the question in your question. If you'd like it is possible to roll it back via the `edit` button (or edit it further). – default Dec 30 '12 at 11:17
  • Writing your own, generic solution for a specific situation that you find yourself in *is* "standard" C++, in the sense that that's idiomatic. C++ is a language for library-writing. – Kerrek SB Dec 30 '12 at 11:26
  • Thanks to all. I just needed to get sure that my template solution is no redundant code. – Raffi Dec 30 '12 at 11:46

3 Answers3

1

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;
}

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;
}

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

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • it is easier to use lambda instead of bind here: `[](double* a, double* b) {return *a < *b;}` – Simon Dec 30 '12 at 13:10
  • @Simon: Except the point was to use existing standard constructs. :) Yours is a perfectly valid example but I was trying to do it without writing "new" types, per se. – Lightness Races in Orbit Dec 30 '12 at 17:11
0

There is no ready-made solution as other answers say. A raw-pointer dereferencing comparator could be improved slightly by making it only available for raw-pointer types with the following explicit template specialisation. I expect this would give slightly better error messages from the compiler in the event that a non-pointer type is used.

template<class T>
struct PointeeLess;

template<class T>
struct PointeeLess<T const *>
{
    bool operator()( T const * a , T const * b ) const { return *a < *b; }
};

OTOH, the template in the question will work for non raw-pointer types that implement operator*.

SimonD
  • 638
  • 5
  • 16
0

I was looking for the same thing in STL but could not find it. Ended up writing my own (handles NULL)

class DeRefPtrLess {

public:

template<typename T>
    bool operator()(const T *l, const T *r) const {
        if (l == NULL
                && r == NULL) {
            return false;
        }
        if (l == NULL) {
            return true;
        }

        return *l < *r;
    }

};

Farid Z
  • 960
  • 1
  • 9
  • 18