HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcppMinor

C++ vectors sort ascending/descending

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
sortvectorsascendingdescending

Problem

How can I eliminate repetition from this code?

std::vector vec;
bool                      descending;
if (descending)
{
    std::sort(vec.begin(),
              vec.end(),
              std::greater());
}
else 
{
    std::sort(vec.begin(),
              vec.end(),
              std::less());
}


I tried

std::sort(vec.begin(),
          vec.end(),
          descending 
             ? std::greater()
             : std::less());


but it fails because greater and less do not evaluate to the same type.

Another question: is it possible to automatically retrieve the required type, std::wstring, from the object vec?

Solution

There are a few ways of going about this. One is creating an aggregate type that can forward to the call to the correct comparator.

template 
struct comp
{
private:
    bool desc;
    std::greater great;
    std::less less;

public:
    explicit comp(bool descending = false)
      : desc(descending)
    { }

    bool operator()(const T& f, const T& g) const
    {
        return (desc) ? great(f, g) : less(f, g);
    }
};


The downside of this is that a comp object will be at least sizeof(greater + less + bool). This may or may not be a problem. Your call to sort would then look something like:

typedef std::wstring str_t;
bool descending = true;
std::sort(vec.begin(), vec.end(), comp(descending));


If the decision is always known at compile time, you can play some template tricks:

template 
struct comp;

template 
struct comp
{
private:
    std::greater great;

public:
    bool operator()(const T& f, const T& g) const
    {
        return great(f, g);
    }
};

template 
struct comp
{
private:
    std::less less;

public:
    bool operator()(const T& f, const T& g) const
    {
        return less(f, g);
    }
};


This requires descending to be const or constexpr:

constexpr bool descending = false;
std::sort(v.begin(), v.end(), comp());


This might be a one step forward, two step back kind of solution. It's quite a bit of code to avoid an if/else - you'll need to decide if this is worth it at all.

Edit: As @LokiAstari pointed out, you can simplify the above with inheritance:

template  
struct comp
  :  std::greater
{ }

template  
struct comp
  : std::less 
{ }

Code Snippets

template <typename T>
struct comp
{
private:
    bool desc;
    std::greater<T> great;
    std::less<T> less;

public:
    explicit comp(bool descending = false)
      : desc(descending)
    { }

    bool operator()(const T& f, const T& g) const
    {
        return (desc) ? great(f, g) : less(f, g);
    }
};
typedef std::wstring str_t;
bool descending = true;
std::sort(vec.begin(), vec.end(), comp<str_t>(descending));
template <typename T, bool desc>
struct comp;

template <typename T>
struct comp<T, true>
{
private:
    std::greater<T> great;

public:
    bool operator()(const T& f, const T& g) const
    {
        return great(f, g);
    }
};

template <typename T>
struct comp<T, false>
{
private:
    std::less<T> less;

public:
    bool operator()(const T& f, const T& g) const
    {
        return less(f, g);
    }
};
constexpr bool descending = false;
std::sort(v.begin(), v.end(), comp<str_t, descending>());
template <typename T> 
struct comp<T, true>
  :  std::greater<T>
{ }

template <typename T> 
struct comp<T, false>
  : std::less<T> 
{ }

Context

StackExchange Code Review Q#26209, answer score: 5

Revisions (0)

No revisions yet.