The Problem
I have a custom type A who natural ordering (having operator<) and multiple alternative orderings (case-sensitive, case-insensitive, etc.). Now I have a std::pair (or std::tuple) of (one or more) of them. How can I compare the std::pair (or std::tuple) as usual for elements of primitive types, and using the function I specified for elements of A?
The Code
The code below doesn't compile:
#include <utility> // std::pair
#include <tuple> // std::tuple
#include <iostream> // std::cout, std::endl
struct A
{
A(char v) : value(v) {}
char value;
};
// LOCATION-1 (explained in the text below)
int main()
{
std::cout
<< "Testing std::pair of primitive types: "
<< (std::pair<char, int>('A', 1)
<
std::pair<char, int>('a', 0))
<< std::endl;
std::cout
<< "Testing std::tuple of primitive types: "
<< (std::tuple<char, int, double>('A', 1, 1.0)
<
std::tuple<char, int, double>('a', 0, 0.0))
<< std::endl;
// This doesn't compile:
std::cout
<< "Testing std::pair of custom types: "
<< (std::pair<A, int>('A', 1)
<
std::pair<A, int>('a', 0))
<< std::endl;
return 0;
}
It is because operator< isn't defined for struct A. Adding it to LOCATION-1 above would solve the problem:
bool operator<(A const& lhs, A const& rhs)
{
return lhs.value < rhs.value;
}
Now, we have an alternative ordering for struct A:
bool case_insensitive_less_than(A const& lhs, A const& rhs)
{
char const lhs_value_case_insensitive
= ('a' <= lhs.value && lhs.value <= 'z'
? (lhs.value + 0x20)
: lhs.value);
char const rhs_value_case_insensitive
= ('a' <= rhs.value && rhs.value <= 'z'
? (rhs.value + 0x20)
: rhs.value);
return lhs_value_case_insensitive < rhs_value_case_insensitive;
}
Supposed we want to keep the original operator< for struct A (the case-sensitive one), how can we compare std::pair<A, int> with this alternative ordering?
I know that adding a specialized version of operator< for std::pair<A, int> solves the problem:
bool operator<(std::pair<A, int> const& lhs, std::pair<A, int> const& rhs)
{
return (case_insensitive_less_than(lhs.first, rhs.first)
? true
: case_insensitive_less_than(rhs.first, lhs.first)
? false
: (lhs.second < rhs.second));
}
However, I consider this a sub-optimal solution.
Firstly, for std::pair, it is easy to re-implement the element-wise comparison, but for std::tuple it might be complicated (dealing with variadic templates) and error-prone.
Secondly, I can hardly believe that it is the best practice way to solve the problem: imagine that we have to define a specialized version of operator< for each of the following classes: std::tuple<A, int, int>, std::tuple<int, A, int>, std::tuple<int, int, A>, std::tuple<A, A, int>, ...
Re-using the well written built-in operator< for std::tuple and plugging-in my less-than for struct A would be what I want. Is it possible? Thanks in advance!
Aucun commentaire:
Enregistrer un commentaire