Perché string :: compare restituisce un int?


102

Perché string::comparerestituisce un intinvece di un tipo più piccolo come shorto char? La mia comprensione è che questo metodo restituisce solo -1, 0 o 1.

Seconda parte, se dovessi progettare un metodo di confronto che confronta due oggetti di tipo Fooe volessi solo restituire -1, 0 o 1, sarebbe usare shorto in chargenerale sarebbe una buona idea?

EDIT: Sono stato corretto, string::comparenon restituisce -1, 0 o 1, in effetti restituisce un valore> 0, <0 o 0. Grazie per avermi tenuto in linea ragazzi.

Sembra che la risposta sia approssimativamente, non c'è motivo di restituire un tipo più piccolo di intperché i valori di ritorno sono "rvalues" e questi "rvalues" non traggono vantaggio dall'essere più piccoli del tipo int (4 byte). Inoltre, molte persone hanno sottolineato che i registri della maggior parte dei sistemi probabilmente avranno intcomunque le dimensioni , poiché questi registri verranno riempiti sia che si dia loro un valore di 1, 2 o 4 byte, non c'è alcun vantaggio reale nel restituire un valore minore.

MODIFICA 2: In effetti sembra che ci possa essere un sovraccarico di elaborazione aggiuntivo quando si utilizzano tipi di dati più piccoli come allineamento, mascheramento, ecc. Il consenso generale è che i tipi di dati più piccoli esistono per conservare la memoria quando si lavora con molti dati, come nel caso di un array.

Ho imparato qualcosa oggi, grazie ancora ragazzi!


Penso che sarebbe meglio se ci fosse un tipo più specifico che potrebbe essere utilizzato per questo. Uno che contiene solo -1, 0 e 1 nello stile di Ada95.
Sachin Kainth

23
La documentazione per il string::compare()collegamento indica chiaramente che il valore restituito è <0, 0 e> 0 -not- -1, 0 e 1.
Capitano Obvlious

6
Quale sarebbe il vantaggio di utilizzare shorto charinvece di int? La maggior parte delle architetture memorizzerà il valore di ritorno di una funzione in un registro e an intsi adatterà a un registro allo stesso modo di un shorto char. E l'utilizzo charper i tipi numerici è sempre una cattiva idea, soprattutto quando è necessario garantire che i valori con segno siano gestiti correttamente.
Cody Grey

7
Capitano Obvlious, il tuo nome e il tuo commento ... Semplicemente impagabile.
Cody Smith

2
L'utilizzo charsarebbe una cattiva idea, poiché il controllo del codice per il valore restituito se è inferiore a zero fallirà sulle piattaforme in cui charnon è firmato.
milleniumbug

Risposte:


113

Innanzitutto, la specifica è che restituirà un valore minore di, uguale o maggiore di 0, non necessariamente -1o 1. In secondo luogo, i valori restituiti sono rvalues, soggetti a promozione integrale, quindi non ha senso restituire qualcosa di più piccolo.

In C ++ (come in C), ogni espressione è un rvalue o un lvalue. Storicamente, i termini si riferiscono al fatto che i valori compaiono a sinistra di un compito, dove i valori possono apparire solo a destra. Oggi, una semplice approssimazione per i tipi non di classe è che un lvalue ha un indirizzo in memoria, un rvalue no. Pertanto, non puoi prendere l'indirizzo di un rvalue e i qualificatori cv (che condizione "accesso") non si applicano. In termini C ++, un valore che non ha un tipo di classe è un valore puro, non un oggetto. Il valore di ritorno di una funzione è un rvalue, a meno che non abbia un tipo di riferimento. (I tipi non di classe che rientrano in un registro verranno quasi sempre restituiti in un registro, ad esempio, piuttosto che in memoria.)

Per i tipi di classe, i problemi sono un po 'più complessi, poiché è possibile chiamare le funzioni membro su un valore. Ciò significa che rvalues ​​deve in effetti avere indirizzi, per il this puntatore, e può essere qualificato cv, poiché la qualifica cv gioca un ruolo nella risoluzione dell'overload. Infine, C ++ 11 introduce diverse nuove distinzioni, al fine di supportare i riferimenti rvalue; anche questi sono applicabili principalmente ai tipi di classe.

La promozione integrale si riferisce al fatto che quando i tipi integrali più piccoli di an intsono usati come rvalues ​​in un'espressione, nella maggior parte dei contesti, saranno promossi int. Quindi, anche se ho una variabile dichiarata short a, b;, nell'espressione a + b, entrambi ae bvengono promossi a intprima che si verifichi l'addizione. Allo stesso modo, se scrivo a < 0, il confronto viene fatto sul valore di a, convertito in un int. In pratica, ci sono pochissimi casi in cui questo fa la differenza, almeno sulle macchine complementi di 2 dove l'aritmetica di interi esegue il wrapping (cioè tutti tranne pochissimi esotici, oggi - penso che i mainframe Unisys siano le uniche eccezioni rimaste). Tuttavia, anche sulle macchine più comuni:

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

dovrebbe dare risultati diversi: il primo è l'equivalente di sizeof( short ), il secondo sizeof( int )(a causa della promozione integrale).

Questi due problemi sono formalmente ortogonali; rvalues ​​e lvalues ​​non hanno nulla a che fare con la promozione integrale. Tranne ... la promozione integrale si applica solo ai rvalues ​​e la maggior parte (ma non tutti) dei casi in cui useresti un rvalue risulterà in una promozione integrale. Per questo motivo, non c'è davvero alcun motivo per restituire un valore numerico in qualcosa di più piccolo di int. C'è anche un'ottima ragione per non restituirlo come tipo di carattere. Gli operatori sovraccarichi, come <<, spesso si comportano in modo diverso per i tipi di carattere, quindi vuoi solo restituire i caratteri come tipi di carattere. (Potresti confrontare la differenza:

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

La differenza è che nel secondo caso l'aggiunta ha provocato la promozione integrale, che si traduce in un diverso sovraccarico di <<da scegliere.


46
Sarebbe bello se tu potessi spiegare di più return values are rvalues, subject to integral promotionnella tua risposta.
Alvin Wong

"i valori di ritorno sono rvalues ​​... quindi non ha senso restituire qualcosa di più piccolo" MI PIACE
masoud

1
@ AlvinWong: vedi le risposte a Perché i caratteri C sono letterali interi anziché caratteri? per ulteriori informazioni di base.
Jesse Good

Vorrei poter fare di nuovo +1 su questo, dopo la superba spiegazione aggiunta dalla tua modifica.
Cody Grey

E se lo fosse stato signed char? Si comporterebbe come un firmato charo sarebbe di tipo diverso?
user541686

41

È intenzionale che non restituisca -1, 0 o 1.

Consente (nota che non è per le stringhe, ma si applica ugualmente alle stringhe)

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

che è molto meno ingombrante di:

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

che è quello che dovresti fare [o qualcosa del genere] se devi restituire -1, 0 o 1.

E funziona anche per tipi più complessi:

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;
}

Nel caso delle stringhe, possiamo fare questo:

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();
}

8
La tua prima comparefunzione ha problemi con overflow che (fortunatamente) non si applicano allo stesso modo se richiede char*ed charè inferiore a int. Ad esempio, se *aè MAX_INTed *bè -1allora *a - *bè UB, ma se l'implementazione sceglie di definire il proprio comportamento, il risultato è quasi certamente negativo.
Steve Jessop

1
Problema con il tuo ultimo esempio: length()restituisce a size_t, che potrebbe essere maggiore di int...
F'x

Sì, potrebbe essere un problema se le tue stringhe sono lunghe più di 2 GB. Ho creato stringhe lunghe 1 GB come test case per memorizzare le cose in una fifo una volta. Ma certo, qualcuno che ha a che fare con una stringa contenente un MPEG codificato come Base64 o qualcosa di simile potrebbe incorrere in quel problema ...
Mats Petersson

@MatsPetersson è più un problema fondamentale, perché la domanda è "perché restituisce un int?"
F'x

Bene, sono sicuro che sia isterico - intendo ragioni storiche - e probabilmente in modo che sia compatibile con strcmp / memcmp e altre operazioni di tipo confronto.
Mats Petersson

25

int è solitamente (ovvero sulla maggior parte dei moderni hardware) un numero intero della stessa dimensione del bus di sistema e / o dei registri della CPU, quella che viene chiamata parola macchina. Pertanto int viene solitamente trasmesso più velocemente rispetto ai tipi più piccoli, perché non richiede allineamento, mascheramento e altre operazioni.

I tipi più piccoli esistono principalmente per consentire l'ottimizzazione dell'utilizzo della RAM per array e strutture. Nella maggior parte dei casi scambiano alcuni cicli della CPU (sotto forma di operazioni di allineamento) per un migliore utilizzo della RAM.

A meno che non sia necessario imporre che il valore di ritorno sia un numero con segno o senza segno di una dimensione centain (char, short ...), è meglio usare int, motivo per cui la libreria standard lo fa.


Ottimo modo per spiegare il lato hardware delle cose in un modo che abbia senso.
Salmo dell'orco33

10

È un C-ismo.

Quando C richiedeva comparefunzioni -type, restituivano sempre un file int. Il C ++ lo ha portato avanti (sfortunatamente).

Tuttavia, restituire un intè realisticamente probabilmente il modo più veloce, poiché generalmente è la dimensione dei registri del sistema in uso. (Deliberatamente vago.)


1
In realtà shorte charpuò imporre penalità sulle prestazioni, ad esempio 255+7ha un valore diverso per a chare intquindi un'implementazione corretta non può necessariamente memorizzare semplicemente a chardove intpuò andare senza prendersi cura di consegnarne la semantica. I compilatori non ottimizzeranno necessariamente l'inefficienza che ciò impone.
Jack Aidley

10

Il metodo in realtà non restituisce un numero intero nell'insieme { -1, 0, 1 }; può effettivamente essere qualsiasi valore integrale.

Perché? Il motivo principale a cui riesco a pensare è che intdovrebbe essere il valore della "dimensione naturale" per l'architettura; le operazioni su valori di questa dimensione sono in genere almeno altrettanto veloci (e in molti casi più veloci) delle operazioni su valori più piccoli o più grandi. Quindi questo è un caso per consentire all'implementazione di un margine di flessibilità sufficiente per utilizzare ciò che è più veloce.


4

se dovessi progettare un metodo di confronto che confronta due oggetti di tipo Foo e volessi solo restituire -1, 0 o 1, sarebbe generalmente una buona idea usare short o char?

Sarebbe un'idea ok. Un modo migliore sarebbe restituire un bool (se si desidera confrontare solo se uguale) o enum (per ulteriori informazioni):

enum class MyResult
{
  EQUAL,
  LESS,
  GREATER
};

MyResult AreEqual( const Foo &foo1, const Foo & foo2 )
{
  // calculate and return result
}

3
"Sarebbe un'idea ok". Hai una motivazione per questo?
jrok

4

Supponiamo che alcune persone stiano cambiando un codice da C a C ++. Hanno deciso di sostituire strcmpa string::compare.

Dal momento che strcmpritorna int, è più facile string::comparetornare int, come regalo.


2

Probabilmente per farlo funzionare più simile strcmpche ha anche questo insieme di valori di ritorno . Se si volesse trasferire il codice, sarebbe probabilmente più intuitivo avere sostituzioni che si chiudano il più vicino possibile.

Inoltre, il valore restituito non è solo -1, 0o 1ma <0, 0o >0.

Inoltre, come è stato detto, poiché il reso è soggetto a promozione integrale , non ha senso ridurlo.


-1

perché un valore restituito booleano può essere solo due valori possibili (vero, falso) e una funzione di confronto può restituire tre valori possibili (minore di, uguale, maggiore di).

Aggiornare

Sebbene sia certamente possibile restituire uno short con segno, se si desidera davvero implementare la propria funzione di confronto, è possibile restituire un valore nibble o struct con due booleani.


7
In nessun punto della domanda si dice qualcosa sulla restituzione di un tipo booleano. Infatti, propone specificamente shorte charcome alternative a int.
Cody Grey
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.