Por que string :: compare retorna um int?

102

Por que string::compareretorna um em intvez de um tipo menor como shortou char? Meu entendimento é que esse método retorna apenas -1, 0 ou 1.

Segunda parte, se eu fosse projetar um método de comparação que compare dois objetos do tipo Fooe quisesse apenas retornar -1, 0 ou 1, usar shortou chargeralmente seria uma boa ideia?

EDIT: Fui corrigido, string::comparenão retorna -1, 0 ou 1, na verdade retorna um valor> 0, <0 ou 0. Obrigado por me manter na linha, pessoal.

Parece que a resposta é aproximadamente, não há razão para retornar um tipo menor do que intporque os valores de retorno são "rvalues" e esses "rvalues" não se beneficiam de serem menores do que o tipo int (4 bytes). Além disso, muitas pessoas apontaram que os registros da maioria dos sistemas provavelmente serão de tamanho de intqualquer maneira, uma vez que esses registros serão preenchidos independentemente de você dar a eles um valor de 1, 2 ou 4 bytes, não há nenhuma vantagem real em retornar um valor menor.

EDIT 2: Na verdade, parece que pode haver sobrecarga de processamento extra ao usar tipos de dados menores, como alinhamento, mascaramento, etc. O consenso geral é que os tipos de dados menores existem para economizar memória ao trabalhar com muitos dados, como no caso de uma matriz.

Aprendi algo hoje, obrigado novamente pessoal!

Cody Smith
fonte
Acho que seria melhor se houvesse um tipo mais específico que pudesse ser usado para isso. Um que contém apenas -1, 0 e 1 no estilo de Ada95.
Sachin Kainth
23
A documentação para o string::compare()seu link afirma claramente que o valor de retorno é <0, 0 e> 0 -não- -1, 0 e 1.
Capitão Óbvio
6
Qual seria a vantagem de usar shortou em charvez de int? A maioria das arquiteturas vai armazenar o valor de retorno de uma função em um registro, e um intcaberá em um registro tão bem quanto um shortou char. E usar charpara tipos numéricos é sempre uma má ideia, especialmente quando você precisa garantir que os valores assinados sejam tratados corretamente.
Cody Gray
7
Capitão Óbvio, seu nome e comentário ... Simplesmente inestimável.
Cody Smith
2
Usar charseria uma má ideia, uma vez que a verificação do código do valor de retorno se for menor que zero irá falhar em plataformas onde charnão está assinado.
milleniumbug

Respostas:

113

Em primeiro lugar, a especificação é que ele retornará um valor menor, igual ou maior que 0, não necessariamente -1ou 1. Em segundo lugar, os valores de retorno são rvalues, sujeitos à promoção integral, portanto, não faz sentido retornar nada menor.

Em C ++ (como em C), cada expressão é um rvalue ou um lvalue. Historicamente, os termos referem-se ao fato de que lvalues ​​aparecem à esquerda de uma atribuição, enquanto rvalues ​​só podem aparecer à direita. Hoje, uma aproximação simples para tipos de não classe é que um lvalue tem um endereço na memória, um rvalue não. Portanto, você não pode obter o endereço de um rvalue, e os qualificadores cv (cuja condição "acesso") não se aplicam. Em termos de C ++, um rvalue que não tem tipo de classe é um valor puro, não um objeto. O valor de retorno de uma função é um rvalue, a menos que tenha um tipo de referência. (Tipos de não classe que cabem em um registro quase sempre serão retornados em um registro, por exemplo, ao invés de na memória.)

Para tipos de classe, os problemas são um pouco mais complexos, devido ao fato de que você pode chamar funções de membro em um rvalue. Isso significa que rvalues ​​devem de fato ter endereços, para o this ponteiro, e podem ser cv-qualificados, visto que a cv-qualification desempenha um papel na resolução da sobrecarga. Finalmente, C ++ 11 introduz várias novas distinções, a fim de oferecer suporte a referências rvalue; estes também são principalmente aplicáveis ​​aos tipos de classe.

A promoção integral refere-se ao fato de que, quando tipos integrais menores do que um intsão usados ​​como rvalues ​​em uma expressão, na maioria dos contextos, eles serão promovidos para int. Portanto, mesmo que eu tenha uma variável declarada short a, b;, na expressão a + b, ambos ae bsão promovidos a intantes de ocorrer a adição. Da mesma forma, se eu escrever a < 0, a comparação é feita no valor de a, convertido em um int. Na prática, existem poucos casos em que isso faz diferença, pelo menos nas máquinas de complementos de 2 onde a aritmética de inteiros envolve (ou seja, todos, exceto alguns exóticos, hoje - acho que os mainframes da Unisys são as únicas exceções restantes). Ainda assim, mesmo nas máquinas mais comuns:

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

deve dar resultados diferentes: o primeiro é equivalente a sizeof( short ), o segundo sizeof( int )(por causa da promoção integral).

Essas duas questões são formalmente ortogonais; rvalues ​​e lvalues ​​não têm nada a ver com promoção integral. Exceto ... a promoção integral só se aplica a rvalues, e a maioria (mas não todos) dos casos em que você usaria um rvalue resultará em promoção integral. Por esse motivo, não há realmente nenhuma razão para retornar um valor numérico em algo menor que int. Há até uma razão muito boa para não devolvê-lo como um tipo de personagem. Operadores sobrecarregados, como <<, geralmente se comportam de maneira diferente para os tipos de caracteres, então você só deseja retornar os caracteres como tipos de caracteres. (Você pode comparar a diferença:

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

A diferença é que, no segundo caso, a adição fez com que ocorresse a promoção integral, o que resulta em uma sobrecarga diferente de <<ser escolhido.

James Kanze
fonte
46
Seria bom se você pudesse explicar mais sobre return values are rvalues, subject to integral promotionem sua resposta.
Alvin Wong
"os valores de retorno são rvalues ​​... então não faz sentido retornar nada menor" LIKE IT
masoud
1
@AlvinWong: Veja as respostas para Por que os caracteres literais C são ints em vez de chars? para mais algumas informações básicas.
Jesse Good
Eu gostaria de poder marcar isto com +1 novamente, após a explicação excelente que sua edição adicionou.
Cody Gray
E se fosse signed char? Ele se comportaria da mesma forma que um assinado charou seria de um tipo diferente?
user541686
41

É intencional que não retorne -1, 0 ou 1.

Ele permite (observe que não é para strings, mas se aplica igualmente a strings)

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

o que é muito menos complicado do que:

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

que é o que você teria que fazer [ou algo nesse sentido] se você tivesse que retornar -1, 0 ou 1.

E também funciona para tipos mais complexos:

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

No caso da string, podemos fazer isso:

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
fonte
8
Sua primeira comparefunção tem problemas com estouro que (felizmente) não se aplicam igualmente se leva char*e charé menor do que int. Por exemplo, se *aé MAX_INTe *bé, -1então *a - *bé UB, mas se a implementação escolher definir seu comportamento, o resultado quase certamente será negativo.
Steve Jessop
1
Problema com seu último exemplo: length()retorna a size_t, que pode ser maior que int...
F'x
Sim, isso pode ser um problema se suas cordas tiverem mais de 2 GB de comprimento. Eu fiz strings de 1 GB como um caso de teste para armazenar coisas em um fifo uma vez. Mas claro, alguém que lida com uma string contendo um MPEG codificado como Base64 ou algo semelhante pode muito bem ter esse problema ...
Mats Petersson
@MatsPetersson é um problema mais fundamental, porque a questão é “por que ele retorna um int?”
F'x
Bem, tenho certeza de que isso é histérico - quero dizer, razões históricas - e provavelmente é compatível com strcmp / memcmp e outras operações de comparação.
Mats Petersson
25

int é geralmente (ou seja, na maioria dos hardwares modernos) um inteiro do mesmo tamanho que o barramento do sistema e / ou os registradores da CPU, o que é chamado de palavra de máquina. Portanto, o int é normalmente transmitido mais rápido do que os tipos menores, porque não requer alinhamento, mascaramento e outras operações.

Os tipos menores existem principalmente para permitir a otimização do uso de RAM para arrays e estruturas. Na maioria dos casos, eles trocam alguns ciclos de CPU (na forma de operações de alinhamento) por um melhor uso de RAM.

A menos que você precise fazer com que seu valor de retorno seja um número com ou sem sinal de um tamanho centain (char, short ...), é melhor usar int, e é por isso que a biblioteca padrão faz isso.

Tobia
fonte
Ótima maneira de explicar o lado do hardware das coisas de uma maneira que faça sentido.
Ogre Salmo 33
10

É um C-ismo.

Quando C requer comparefunções -type, elas sempre retornam um int. C ++ apenas levou isso adiante (infelizmente).

No entanto, retornar um inté realisticamente provavelmente a maneira mais rápida, pois geralmente é o tamanho dos registros do sistema em uso. (Deliberadamente vago.)

Alex Chamberlain
fonte
1
Na verdade, shorte charpode impor penalidades de desempenho, por exemplo, 255+7tem um valor diferente para a chare, intportanto, uma implementação correta não pode necessariamente simplesmente armazenar a charonde um intpode ir sem cuidar de manipular sua semântica. Os compiladores não necessariamente otimizarão a ineficiência que isso impõe.
Jack Aidley
10

O método não retorna realmente um inteiro no conjunto { -1, 0, 1 }; na verdade, pode ser qualquer valor integral.

Por quê? A principal razão que consigo pensar é que esse inté o valor de "tamanho natural" da arquitetura; operações em valores desse tamanho são normalmente pelo menos tão rápidas (e em muitos casos mais rápidas) do que operações em valores menores ou maiores. Portanto, este é um caso de permitir a implementação folga suficiente para usar o que for mais rápido.

Jon
fonte
4

se eu fosse projetar um método de comparação que compare dois objetos do tipo Foo e só quisesse retornar -1, 0 ou 1, usar short ou char geralmente seria uma boa ideia?

Seria uma boa ideia. A melhor maneira seria retornar um bool (se quiser apenas comparar se for igual) ou enum (para obter mais informações):

enum class MyResult
{
  EQUAL,
  LESS,
  GREATER
};

MyResult AreEqual( const Foo &foo1, const Foo & foo2 )
{
  // calculate and return result
}
BЈовић
fonte
3
"Seria uma boa ideia". Você tem uma justificativa para isso?
jrok
4

Suponha que algumas pessoas estejam mudando um código de C para C ++. Eles decidiram substituir strcmppor string::compare.

Desde que strcmpretorna int, é mais fácil string::comparedevolver int, como um presente.

Masoud
fonte
2

Provavelmente para fazer funcionar mais como strcmpaquele que também tem esse conjunto de valores de retorno . Se você quisesse portar código, provavelmente seria mais intuitivo ter substitutos que se separassem o mais próximo possível.

Além disso, o valor de retorno não é apenas -1, 0ou , 1mas <0, 0ou >0.

Além disso, conforme mencionado, uma vez que o retorno está sujeito a promoção integral , não faz sentido diminuí-lo.

Shafik Yaghmour
fonte
-1

porque um valor de retorno booleano só pode ter dois valores possíveis (verdadeiro, falso) e uma função de comparação pode retornar três valores possíveis (menor que, igual, maior que).

Atualizar

Embora seja certamente possível retornar um short assinado, se você realmente quiser implementar sua própria função de comparação, poderá retornar um valor nibble ou struct com dois booleanos.

MDMoore313
fonte
7
Em nenhum lugar da pergunta diz algo sobre retornar um tipo booleano. Na verdade, ele propõe especificamente shorte charcomo alternativas para int.
Cody Gray