¿Por qué string :: compare devuelve un int?

102

¿Por qué string::comparedevuelve un en intlugar de un tipo más pequeño como shorto char? Tengo entendido que este método solo devuelve -1, 0 o 1.

Segunda parte, si tuviera que diseñar un método de comparación que compare dos objetos de tipo Fooy solo quisiera devolver -1, 0 o 1, ¿ sería una buena idea usar shorto en chargeneral?

EDITAR: Me han corregido, string::compareno devuelve -1, 0 o 1, de hecho devuelve un valor> 0, <0 o 0. Gracias por mantenerme en línea, chicos.

Parece que la respuesta es aproximadamente, no hay razón para devolver un tipo más pequeño que intporque los valores devueltos son "rvalues" y esos "rvalues" no se benefician de ser más pequeños que el tipo int (4 bytes). Además, muchas personas señalaron que los registros de la mayoría de los sistemas probablemente serán de tamaño grande de inttodos modos, ya que estos registros se llenarán si les da un valor de 1, 2 o 4 bytes, no hay una ventaja real en devolver un menor valor.

EDICIÓN 2: De hecho, parece que puede haber una sobrecarga de procesamiento adicional cuando se utilizan tipos de datos más pequeños, como alineación, enmascaramiento, etc. El consenso general es que los tipos de datos más pequeños existen para conservar la memoria cuando se trabaja con muchos datos, como en el caso de una matriz.

Aprendí algo hoy, ¡gracias de nuevo chicos!

Cody Smith
fuente
Creo que sería mejor si hubiera un tipo más específico que pudiera usarse para esto. Uno que contiene solo -1, 0 y 1 al estilo de Ada95.
Sachin Kainth
23
La documentación para string::compare()su enlace establece claramente que el valor de retorno es <0, 0 y> 0 -no- -1, 0 y 1.
Captain Obvlious
6
¿Cuál sería la ventaja de usar shorto en charlugar de int? La mayoría de las arquitecturas almacenarán el valor de retorno de una función en un registro, y una intencajará en un registro tan bien como una shorto char. Y usar charpara tipos numéricos siempre es una mala idea, especialmente cuando necesita garantizar que los valores firmados se manejen correctamente.
Cody Gray
7
Capitán Obvlious, su nombre y comentario ... Simplemente no tiene precio.
Cody Smith
2
Usar charsería una mala idea, ya que la verificación del código para el valor de retorno si es menor que cero fallará en plataformas donde charno esté firmado.
milleniumbug

Respuestas:

113

Primero, la especificación es que devolverá un valor menor, igual o mayor que 0, no necesariamente -1o 1. En segundo lugar, los valores devueltos son valores r, sujetos a promoción integral, por lo que no tiene sentido devolver algo más pequeño.

En C ++ (como en C), cada expresión es un rvalue o un lvalue. Históricamente, los términos se refieren al hecho de que los valores l aparecen a la izquierda de una asignación, mientras que los valores r solo pueden aparecer a la derecha. Hoy en día, una aproximación simple para los tipos que no son de clase es que un lvalue tiene una dirección en la memoria, un rvalue no. Por lo tanto, no puede tomar la dirección de un rvalue y los calificadores cv (cuya condición es "acceso") no se aplican. En términos de C ++, un rvalue que no tiene tipo de clase es un valor puro, no un objeto. El valor de retorno de una función es un rvalue, a menos que tenga un tipo de referencia. (Los tipos que no son de clase que caben en un registro casi siempre se devolverán en un registro, por ejemplo, en lugar de en la memoria).

Para los tipos de clase, los problemas son un poco más complejos, debido al hecho de que puede llamar a funciones miembro en un rvalue. Esto significa que los rvalues ​​deben tener direcciones, para el this puntero, y pueden estar calificados cv, ya que la calificación cv juega un papel en la resolución de sobrecargas. Finalmente, C ++ 11 introduce varias distinciones nuevas, con el fin de admitir referencias rvalue; estos también son principalmente aplicables a tipos de clases.

La promoción integral se refiere al hecho de que cuando intse utilizan tipos integrales menores que an como valores r en una expresión, en la mayoría de los contextos, se promoverán a int. Entonces, incluso si tengo una variable declarada short a, b;, en la expresión a + b, se promueven ambos ay antes de que ocurra la adición. De manera similar, si escribo , la comparación se realiza sobre el valor de , convertido a un . En la práctica, hay muy pocos casos en los que esto hace una diferencia, al menos en máquinas de complementos de 2 donde la aritmética de enteros se ajusta (es decir, todos menos unos pocos exóticos, hoy en día, creo que los mainframes de Unisys son las únicas excepciones que quedan). Aún así, incluso en las máquinas más comunes:binta < 0aint

short a = 1;
std::cout << sizeof( a ) << std::endl;
std::cout << sizeof( a + 0 ) << std::endl;

debe dar resultados diferentes: el primero es el equivalente de sizeof( short ), el segundo sizeof( int )(por promoción integral).

Estos dos temas son formalmente ortogonales; rvalues ​​y lvalues ​​nada tienen que ver con la promoción integral. Excepto ... la promoción integral solo se aplica a rvalues, y la mayoría (pero no todos) de los casos en los que usaría un rvalue resultará en una promoción integral. Por esta razón, realmente no hay ninguna razón para devolver un valor numérico en algo menor que int. Incluso hay una muy buena razón para no devolverlo como tipo de carácter. Los operadores sobrecargados, como <<, a menudo se comportan de manera diferente para los tipos de caracteres, por lo que solo desea devolver caracteres como tipos de caracteres. (Puede comparar la diferencia:

char f() { return 'a'; }
std::cout << f() << std::endl;      //  displays "a"
std::cout << f() + 0 << std::endl;  //  displays "97" on my machine

La diferencia es que en el segundo caso, la adición ha provocado que se produzca una promoción integral, lo que da como resultado una sobrecarga diferente de <<elegida.

James Kanze
fuente
46
Sería bueno si pudiera explicar más return values are rvalues, subject to integral promotionen su respuesta.
Alvin Wong
"los valores devueltos son rvalues ​​... así que no tiene sentido devolver algo más pequeño" COMO ÉL
masoud
1
@AlvinWong: Vea las respuestas a ¿Por qué los caracteres C literales son ints en lugar de caracteres? para obtener más información de fondo.
Jesse Good
Ojalá pudiera hacer +1 en esto nuevamente, después de la excelente explicación que agregó su edición.
Cody Gray
¿Y si lo fuera signed char? ¿Se comportaría igual que una firmada charo sería de un tipo diferente?
user541686
41

Es intencional que no devuelva -1, 0 o 1.

Permite (tenga en cuenta que esto no es para cadenas, pero se aplica igualmente a cadenas)

int compare(int *a, int *b)
{
   return *a - *b;
}

que es mucho menos engorroso que:

int compare(int *a, int *b)
{
   if (*a == *b) return 0;
   if (*a > *b) return 1;
   return -1;
}

que es lo que tendrías que hacer [o algo por el estilo] si tienes que devolver -1, 0 o 1.

Y también funciona para tipos más complejos:

class Date
{
    int year;
    int month;
    int day;
}

int compare(const Date &a, const Date &b)
{
   if (a.year != b.year) return a.year - b.year;
   if (a.month != b.month) return a.month - b.month;
   return a.day - b.day;
}

En el caso de la cadena, podemos hacer esto:

int compare(const std::string& a, const std::string& b)
{
   int len = min(a.length(), b.length());

   for(int i = 0; i < len; i++)
   {
      if (a[i] != b[i]) return a[i] - b[i];
   }
   // We only get here if the string is equal all the way to one of them
   // ends. If the length isn't equal, "longest" wins. 
   return a.length() - b.length();
}
Mats Petersson
fuente
8
Su primera comparefunción tiene problemas con el desbordamiento que (afortunadamente) no se aplican igualmente si toma char*y chares más pequeño que int. Por ejemplo, si *aes MAX_INTy *bes -1entonces *a - *bes UB, pero si la implementación elige definir su comportamiento, el resultado es casi seguro que es negativo.
Steve Jessop
1
Problema con su último ejemplo: length()devuelve a size_t, que puede ser mayor que int
F'x
Sí, eso puede ser un problema si sus cadenas tienen más de 2 GB de longitud. He hecho cadenas largas de 1GB como caso de prueba para almacenar cosas en un quince una vez. Pero claro, alguien que se ocupe de una cadena que contenga un MPEG codificado como Base64 o algo así puede encontrarse con ese problema ...
Mats Petersson
@MatsPetersson es más un problema fundamental, porque la pregunta es "¿por qué devuelve un int?"
F'x
Bueno, estoy seguro de que eso es histérico, me refiero a razones históricas, y probablemente sea compatible con strcmp / memcmp y otras operaciones de tipo de comparación.
Mats Petersson
25

int es generalmente (es decir, en la mayoría de hardware moderno) un número entero del mismo tamaño que el bus del sistema y / o los registros de la cpu, lo que se denomina palabra máquina. Por lo tanto, int generalmente se transmite más rápido que los tipos más pequeños, porque no requiere alineación, enmascaramiento ni otras operaciones.

Los tipos más pequeños existen principalmente para permitir la optimización del uso de RAM para matrices y estructuras. En la mayoría de los casos, intercambian algunos ciclos de CPU (en forma de operaciones de alineación) por un mejor uso de la RAM.

A menos que necesite exigir que su valor de retorno sea un número firmado o sin firmar de un tamaño centain (char, short…), es mejor usar int, por lo que la biblioteca estándar lo hace.

Tobia
fuente
Excelente manera de explicar el lado del hardware de una manera que tenga sentido.
Ogre Psalm33
10

Es un C-ismo.

Cuando C requería comparefunciones de tipo -tipo, siempre devolvían un int. C ++ simplemente llevó eso adelante (desafortunadamente).

Sin embargo, devolver un intes probablemente la forma más rápida, ya que generalmente es el tamaño de los registros del sistema en uso. (Deliberadamente vago.)

Alex Chamberlain
fuente
1
En realidad, shorty charpuede imponer penalizaciones de rendimiento, por ejemplo, 255+7tiene un valor diferente para ay charan, intpor lo que una implementación correcta no necesariamente puede simplemente almacenar a chardonde intpuede ir sin ocuparse de entregar su semántica. Los compiladores no necesariamente optimizarán la ineficiencia que esto impone.
Jack Aidley
10

El método en realidad no devuelve un número entero en el conjunto { -1, 0, 1 }; en realidad, puede ser cualquier valor integral.

¿Por qué? La razón principal en la que puedo pensar es que intse supone que es el valor de "tamaño natural" para la arquitectura; las operaciones con valores de este tamaño suelen ser al menos tan rápidas (y en muchos casos más rápidas) que las operaciones con valores más pequeños o más grandes. Así que este es un caso de permitir que la implementación sea lo suficientemente floja para usar lo que sea más rápido.

Jon
fuente
4

si tuviera que diseñar un método de comparación que compare dos objetos de tipo Foo y solo quisiera devolver -1, 0 o 1, ¿sería una buena idea usar short o char?

Estaría bien la idea. Una mejor manera sería devolver un bool (si solo desea comparar si es igual), o enum (para obtener más información):

enum class MyResult
{
  EQUAL,
  LESS,
  GREATER
};

MyResult AreEqual( const Foo &foo1, const Foo & foo2 )
{
  // calculate and return result
}
BЈовић
fuente
3
"Estaría bien idea". ¿Tiene una justificación para eso?
jrok
4

Supongamos que algunas personas están cambiando un código de C a C ++. Decidieron reemplazar strcmpa string::compare.

Desde las strcmpdevoluciones int, es más fácil string::comparedevolverlas int, como regalo.

masoud
fuente
2

Probablemente para que funcione más como lo strcmpque también tiene este conjunto de valores de retorno . Si quisiera portar el código, probablemente sería más intuitivo tener reemplazos que se escinden lo más cerca posible.

Además, el valor de retorno no es solo -1, 0o 1sino <0, 0o >0.

Asimismo, como se mencionó dado que la devolución está sujeta a promoción integral no tiene sentido hacerla más pequeña.

Shafik Yaghmour
fuente
-1

porque un valor de retorno booleano solo puede ser dos valores posibles (verdadero, falso) y una función de comparación puede devolver tres valores posibles (menor que, igual, mayor que).

Actualizar

Aunque ciertamente es posible devolver un short firmado, si realmente desea implementar su propia función de comparación, puede devolver un valor nibble o struct con dos valores booleanos.

MDMoore313
fuente
7
En ninguna parte de la pregunta dice nada sobre la devolución de un tipo booleano. De hecho, propone específicamente shorty charcomo alternativas a int.
Cody Gray