c++ - que - operadores relacionales
Implementar operadores de comparación a través de ''tuple'' y ''tie'', ¿una buena idea? (4)
(Nota: la tuple
y el tie
se pueden tomar de Boost o C ++ 11).
Al escribir estructuras pequeñas con solo dos elementos, a veces tiendo a elegir un std::pair
, ya que todo lo importante ya está hecho para ese tipo de datos, como operator<
para ordenamiento estricto-débil.
Las desventajas son los nombres de variables bastante inútiles. Incluso si yo mismo hubiera creado ese typedef
, no recordaré 2 días después qué fue exactamente el first
y el second
, especialmente si ambos son del mismo tipo. Esto empeora para más de dos miembros, ya que el pair
anidación es una mierda.
La otra opción para eso es una tuple
, ya sea de Boost o C ++ 11, pero eso realmente no se ve mejor y más claro. Así que vuelvo a escribir las estructuras yo mismo, incluidos los operadores de comparación necesarios.
Dado que especialmente el operator<
puede ser bastante engorroso, pensé en eludir todo este lío simplemente confiando en las operaciones definidas para la tuple
:
Ejemplo de operator<
, por ejemplo, para ordenamiento estricto-débil:
bool operator<(MyStruct const& lhs, MyStruct const& rhs){
return std::tie(lhs.one_member, lhs.another, lhs.yet_more) <
std::tie(rhs.one_member, rhs.another, rhs.yet_more);
}
( tie
hace una tuple
de T&
referencias de los argumentos pasados.)
Editar : La sugerencia de @DeadMG para heredar de forma privada de la tuple
no es mala, pero tiene algunos inconvenientes:
- Si los operadores son autónomos (posiblemente amigos), necesito heredar públicamente
- Con el casting, mis funciones / operadores (
operator=
specific) se pueden pasar fácilmente por alto - Con la solución de
tie
, puedo omitir a ciertos miembros si no importan para el pedido
¿Hay algún inconveniente en esta implementación que deba tener en cuenta?
En mi opinión, todavía no está abordando el mismo problema que el std::tuple
resuelve, es decir, tiene que saber cuántos y el nombre de cada variable miembro, lo está duplicando dos veces en la función. Puede optar por private
herencia private
.
struct somestruct : private std::tuple<...> {
T& GetSomeVariable() { ... }
// etc
};
Para empezar, este enfoque es un poco más complicado, pero solo se mantienen las variables y los nombres en un solo lugar, en lugar de en cada lugar para cada operador que desee sobrecargar.
Esto ciertamente hará que sea más fácil escribir un operador correcto que rodarlo usted mismo. Yo diría que solo considere un enfoque diferente si el perfil muestra que la operación de comparación es una parte de su aplicación que consume mucho tiempo. De lo contrario, la facilidad de mantener esto debería superar cualquier posible problema de rendimiento.
He encontrado este mismo problema y mi solución usa plantillas variadas de c ++ 11. Aquí viene el código:
La parte .h:
/***
* Generic lexicographical less than comparator written with variadic templates
* Usage:
* pass a list of arguments with the same type pair-wise, for intance
* lexiLessthan(3, 4, true, false, "hello", "world");
*/
bool lexiLessthan();
template<typename T, typename... Args>
bool lexiLessthan(const T &first, const T &second, Args... rest)
{
if (first != second)
{
return first < second;
}
else
{
return lexiLessthan(rest...);
}
}
Y el .cpp para el caso base sin argumentos:
bool lexiLessthan()
{
return false;
}
Ahora tu ejemplo se convierte en:
return lexiLessthan(
lhs.one_member, rhs.one_member,
lhs.another, rhs.another,
lhs.yet_more, rhs.yet_more
);
Si planea usar más de una sobrecarga de operador, o más métodos de tupla, recomendaría hacer de tuple un miembro de la clase o derivar de tupla. De lo contrario, lo que estás haciendo es mucho más trabajo. Al decidir entre los dos, una pregunta importante para responder es: ¿Quieres que tu clase sea una tupla? Si no, recomendaría contener una tupla y limitar la interfaz mediante delegación.
Podría crear accesos para "cambiar el nombre" de los miembros de la tupla.