How can I eliminate repetition from this code?
std::vector<std::wstring> vec;
bool descending;
if (descending)
{
std::sort(vec.begin(),
vec.end(),
std::greater<std::wstring>());
}
else
{
std::sort(vec.begin(),
vec.end(),
std::less<std::wstring>());
}
I tried
std::sort(vec.begin(),
vec.end(),
descending
? std::greater<std::wstring>()
: std::less<std::wstring>());
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
?
4 Answers 4
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 <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);
}
};
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<str_t>(descending));
If the decision is always known at compile time, you can play some template tricks:
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);
}
};
This requires descending
to be const
or constexpr
:
constexpr bool descending = false;
std::sort(v.begin(), v.end(), comp<str_t, descending>());
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 <typename T>
struct comp<T, true>
: std::greater<T>
{ }
template <typename T>
struct comp<T, false>
: std::less<T>
{ }
-
2\$\begingroup\$ Last example can be simplified to:
template <typename T> struct comp<T, true>: std::greater<T> {}
andtemplate <typename T> struct comp<T, false>: std::less<T> {}
\$\endgroup\$Loki Astari– Loki Astari2013年05月16日 11:50:25 +00:00Commented May 16, 2013 at 11:50 -
\$\begingroup\$ @LokiAstari Good point. Added. \$\endgroup\$Yuushi– Yuushi2013年05月16日 12:05:25 +00:00Commented May 16, 2013 at 12:05
not an answer but extended comment that needs space:
typedef std::vector<std::wstring> Vec;
Vec vec;
std::sort(vec.begin(), vec.end(), std::greater<Vec::value_type>());
Using std::function
seems to work here with g++ 4.5.2 and -std=c++0x
:
#include <algorithm>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char* argv[]) {
std::function<bool(std::string,std::string)> cmp;
if (false)
cmp = std::greater<std::string>();
else
cmp = std::less<std::string>();
std::vector<std::string> vec = { "2", "1", "3" };
std::sort(vec.begin(), vec.end(), cmp);
for (int i = 0; i < vec.size(); ++i) {
std::cout << vec[i] << std::endl;
}
}
You can also use the ternary operator if you cast your compare objects to the same type:
typedef std::function<bool(std::string,std::string)> cmp_t;
const auto cmp =
true ? static_cast<cmp_t>(std::greater<std::string>()) :
static_cast<cmp_t>(std::less<std::string>());
Just create a simple helper function template:
template<typename Iter>
void sort_with_order(Iter i0, Iter i1, bool descending)
{
typedef typename std::iterator_traits<Iter>::value_type value_type;
if (descending)
{
std::sort(i0, i1, std::greater<value_type>());
}
else
{
std::sort(i0, i1, std::less<value_type>());
}
}
Then call it like this:
std::vector<std::wstring> vec;
bool descending;
// ...
sort_with_order(vec.begin(), vec.end(), descending);
-
\$\begingroup\$ Of course, this is how I did it, still not optimal. \$\endgroup\$Felix Dombek– Felix Dombek2013年05月17日 13:06:53 +00:00Commented May 17, 2013 at 13:06
vec::value_type
\$\endgroup\$