I would like to sort an std::vector containing pointers to SampleInCache objects, using the std::sort algorithm. But since the vector contains no objects, but pointers, I can not do that by simply overloading the <-operator of my SampleInCache class.
I tried this :
//adding this to SampleInCache.h
template <>
struct std::greater
{
bool operator()(const SampleInCache* leftVal, const SampleInCache* rightVal) const
{
//The sorting is actually more complex,
//but I first tried sorting only based on the last_time_used member of SampleInCache class
return leftVal->last_time_used < rightVal->last_time_used;
}
};
But then I get this error :
"error C2913: explicit specialization; 'std::greater' is not a specialization of a class template"
Re: Using std::sort on vector of pointers to objects
I don't think that you're meant to specialise std::greater. A simple way would be to overload operator < in your class and then use the following template:
Code:
template <typename T>
struct pointer_less_than
{
const bool operator()(const T *a, const T * b) const {
// check for 0
if (a == 0) {
return b != 0; // if b is also 0, then they are equal, hence a is not < than b
} else if (b == 0) {
return false;
} else {
return *a < *b;
}
}
};
// later
std::vector<sometype *> v;
std::sort(v.begin(), v.end(), pointer_less_than<sometype>());
Get this small utility to do basic syntax highlighting in vBulletin forums (like Codeguru) easily.
Supports C++ and VB out of the box, but can be configured for other languages.
Re: Using std::sort on vector of pointers to objects
Joanna,
if the comparison you do in operator() is more complex, as you stated, it might be better to write a new functor afterall, specialized for your purpose.
Note that you can still use pointers and boost::lambda, this has the advantage that your functor now works on objects AND pointers to those.
Code:
#include <boost/lambda/lambda.hpp>
using namespace boost::lambda;
std::vector<SampleInCache> v1;
// populate v1 ...
std::sort( v1.begin(), v1.end(), sample_in_cache_less() );
// create vector of pointers to objects in v1
std::vector<SampleInCache*> v2( v1.size() );
std::transform( v1.begin(), v1.end(), v2.begin(), &_1 );
// now sort it with your predicate applying to the pointees!
std::sort( v2.begin(), v2.end(), bind( sample_in_cache_less(), *_1, *_2 ) );
The _1 and _2 are placeholders for the arguments passed to your operator(). boost::lambda::bind then creates an adaptor for your functor which will work on pointers. This adaptor (which itself is a functor) is then passed as the predicate function to std::sort.
Hope that helps!
Last edited by matthias_k; April 20th, 2005 at 02:46 AM.
- Matthias
"C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." - Bjarne Stroustrup
Re: Using std::sort on vector of pointers to objects
Thanks for the reactions.
Originally Posted by Yves M
A simple way would be to overload operator < in your class and then use the following template: ...
I tried out your solution, because of its simplicity. Since I already had overloaded the <-operator in my SampleInCache class, I only had to add this :
Code:
//In SampleInCache.cpp
template <SampleInCache>
struct pointer_less_than
{
const bool operator()(const SampleInCache *a, const SampleInCache * b) const {
// check for 0
if (a == 0)
{
return b != 0; // if b is also 0, then they are equal, hence a is not < than b
}
else if (b == 0)
{
return false;
}
else
{
return *a < *b;
}
}
};
SampleInCache::TestMethod(){
...
//testlist is declared as list<SampleInCache*> testlist;
std::sort(testlist.begin(),testlist.end(),pointer_less_than<SampleInCache>());
...
}
But I get these compile errors (all on the last line "std::sort(....);":
error C2780: 'void std::sort(_RanIt,_RanIt)' : expects 2 arguments - 3 provided
error C2955: 'pointer_less_than' : use of class template requires template argument list
error C2975: 'pointer_less_than' : invalid template argument for 'function-parameter', compile-time evaluatable constant expression expected
That doesn't make sense. Either you want it to be a normal class, or a template. If it ought to be a template, you have to parametrize its type:
Code:
//In SampleInCache.cpp
template <typename T>
struct pointer_less_than
: std::binary_function(bool, const T*, const T*>
{
const bool operator()(const T *a, const T * b) const {
// check for 0
if (a == 0)
{
return b != 0; // if b is also 0, then they are equal, hence a is not < than b
}
else if (b == 0)
{
return false;
}
else
{
return *a < *b;
}
}
};
However, this approach is bad for several reasons. It you're not proficient with templates, I'm gonna skip this though. One reason is, that the type T provided might be a reference type, and the argument list in your operator() would evaluate to "const Type& *a " and this is illegal. You can only solve this issue using template meta programming, and I doubt you want to deal with that.
So just do as in my example, make your functor a normal class and declare the arguments to be pointers:
One note: Don't forget to derive your functor classes from std::binary_function if they take two arguments and from std::unary_function is they take one argument. Those base classes simply provide some necessary typedefs to make your functor what is called an "adaptable binary/unary function", as defined by the C++ standard. This makes sure they will work flawlessly together with STL algorithms.
Last edited by matthias_k; April 20th, 2005 at 02:46 AM.
- Matthias
"C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." - Bjarne Stroustrup
Re: Using std::sort on vector of pointers to objects
However, this approach is bad for several reasons. It you're not proficient with templates, I'm gonna skip this though. One reason is, that the type T provided might be a reference type, and the argument list in your operator() would evaluate to "const Type& *a " and this is illegal.
If anyone is interested in a solution which avoids these issues, check out this generic adaptor I have written using boost template metaprogramming facilities. It is completely generic, in that it abstracts from e.g. whether a function pointer or a functor is passed, and solves issues arising from type incompatibilities such as the one mentioned above and of course the reference-to-reference problem.
Thanks to the guys from boost, this wasn't such a big deal afterall (god bless them).
It hasn't been tested a lot, so if anyone should stumble over inconsistencies of any kind, please report them to me.
EDIT: Note that this template can do nothing what lambda can't. It's probably only useful if you don't have access to boost::lambda (while on the other hand, this template also uses boost headers, so that's probably a rare case, ah well).
Last edited by matthias_k; April 19th, 2005 at 02:37 PM.
- Matthias
"C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." - Bjarne Stroustrup
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.