A operação "false <true" está bem definida?


153

A especificação C ++ define:

  1. a existência do operador 'less than' para parâmetros booleanos e, em caso afirmativo,
  2. o resultado das 4 permutações de parâmetros?

Em outras palavras, os resultados das seguintes operações são definidos pela especificação?

false < false
false < true
true < false
true < true

Na minha instalação (Centos 7, gcc 4.8.2), o código abaixo mostra o que eu esperaria (dado o histórico de C de representar falso como 0 e verdadeiro como 1):

false < false = false
false < true = true
true < false = false
true < true = false

Embora eu tenha certeza de que a maioria dos compiladores (todos?) Fornecerá a mesma saída, isso é legislado pela especificação C ++? Ou um compilador ofuscante, mas compatível com as especificações, pode decidir que verdadeiro é menor que falso?

#include <iostream>

const char * s(bool a)
{
  return (a ? "true" : "false");
}

void test(bool a, bool b)
{
  std::cout << s(a) << " < " << s(b) << " = " << s(a < b) << std::endl;
}

int main(int argc, char* argv[])
{
  test(false, false);
  test(false, true);
  test(true, false);
  test(true, true);
  return 0;
}

6
@ Exterior Existem usos válidos. Tal como a utilização std::minem std::vector<bool>como &&.
Angew não está mais orgulhoso de SO

19
@ Exterior Se você conseguir descobrir uma boa pergunta que ainda não foi feita depois de todos esses anos de StackOverflow, você merece alguns pontos. Não é trollar.
Mark Ransom

35
@ Interior A motivação para perguntar é genuína: sou relativamente novo em C ++ (vindo de C) e quero armazenar alguns objetos em um std :: set <>. Minha implementação do operador <do meu objeto é baseada principalmente em uma propriedade booleana do objeto, seguida por outras propriedades de identificação secundárias. Ao iterar sobre o conjunto, quero ter certeza de que os objetos 'falsos' vêm primeiro. Embora ele funcione para mim aqui e agora, estou procurando por garantias de que ele funcione em várias plataformas (incluindo as incorporadas) sem ter que recorrer desnecessariamente ao uso de (a? 1: 0) ou similar, no objeto < operador.
Duncan

26
Uma conseqüência perturbadora é que p <= qsignifica p implies qquando pe qé do tipo bool!
Theodore Norvell

4
@Technophile Presumivelmente, o que é preocupante é que isso <=pode ser inadvertidamente lido como uma seta esquerda e que o lado direito "somente se" (isto é, [implica materialmente ") às vezes é tipicamente digitado ou informalmente escrito de forma semelhante a =>(isto é, com um eixo duplo semelhante =) . Às vezes, uma seta esquerda é lida como "se", embora eu acredite que isso seja muito menos comum do que o uso de uma seta direita para "somente se".
Eliah Kagan

Respostas:


207

TL; DR:

As operações são bem definidas de acordo com o rascunho do padrão C ++.

Detalhes

Podemos ver isso indo para a seção preliminar de 5.9 operadores do C ++ , que diz ( ênfase a minha adiante ):

Os operandos devem ter aritmética , enumeração ou tipo de ponteiro ou tipo std :: nullptr_t. Os operadores <(menor que),> (maior que), <= (menor ou igual a) e> = (maior ou igual a) todos produzem falso ou verdadeiro. O tipo do resultado é booleano

e bools são tipos aritemáticos de 3.9.1 Tipos fundamentais

Os tipos bool , char, char16_t, char32_t, wchar_t e os tipos de números inteiros assinados e não assinados são chamados coletivamente de tipos integrais.

e

Tipos integrais e flutuantes são chamados coletivamente de tipos aritméticos.

e truee falsesão literais booleanos de 2.14.6literais booleanos:

boolean-literal:
    false
    true

Voltando à seção 5.9para ver a mecânica dos operadores relacionais, ele diz:

As conversões aritméticas usuais são realizadas em operandos do tipo aritmético ou de enumeração.

as conversões aritméticas comuns são abordadas na seção 5que diz:

Caso contrário, as promoções integrais (4.5) devem ser realizadas nos dois operandos

e a seção 4.5diz:

Um pré-valor do tipo bool pode ser convertido em um pré-valor do tipo int, com false se tornando zero e true se tornando um.

e então as expressões:

false < false
false < true
true < false
true < true

O uso dessas regras se torna:

0 < 0
0 < 1
1 < 0
1 < 1

6
Bom, isso é tão explícito quanto qualquer resposta poderia ser, embora ainda seja fácil de ler. Um ponto: acho que você colocou em negrito o "tipo" errado: "Os operandos devem ter aritmética , enumeração ou tipo de ponteiro ou tipo std :: nullptr_t." Adicionar parênteses para maior clareza fornece o tipo ((aritmética, enumeração ou ponteiro)) ou (tipo std :: nullptr_t).

Não que isso mude sua resposta, mas N3485 [over.built] / 12: Para cada par de tipos aritméticos promovidos L e R, existem funções candidatas a operadores do formulário ... bool operator <(L, R); - Os argumentos não são promovidos antes mesmo das regras que você cita?
chris

@chris Eu não estou super familiarizado com essa seção, então eu teria que pensar sobre isso, mas não acho que a resposta mude do que posso ver.
Shafik Yaghmour 18/03/2015

Sim, a promoção é a primeira coisa a acontecer de qualquer maneira.
chris

63

Os valores booleanos estão sujeitos às promoções inteiras usuais, com falsedefinido como 0e truedefinido como 1. Isso torna todas as comparações bem definidas.


2
... e operadores relacionais são especificados para executar as conversões aritméticas comuns (que incluem promoções com números inteiros) em operandos do tipo aritmético ou de enumeração.
TC

5
Eu gosto que essa resposta seja mais curta que a de Shafik, mas acho que o ponto principal que falseé definido como 0e trueé definido como 1 no padrão (e não apenas pela prática comum) precisa de evidências para apoiá-lo.
KRyan #

@KRyan o que, você não vai acreditar na minha palavra? :) Antes que houvesse um booltipo, antes mesmo que houvesse C ++, o resultado de uma operação booleana era definido como 0falso e 1verdadeiro. Eu não ficaria surpreso se você puder encontrá-lo em K + R.
Mark Ransom

1
@KRyan Não posso voltar tão longe quanto K + R, mas descobri minha cópia da norma ANSI C de 1990. A Seção 6.3.8 diz que "Cada um dos operadores <(menor que), >(maior que), <=(menor ou igual a) e >=(maior ou igual a) deve produzir 1 se a relação especificada for verdadeira e 0 se for false. O resultado tem o tipo int".
Mark Ransom

1
O maior problema do IIRC foi que, em K&R, enum bool { false = 0, true = 1}era legal, mas não definiu um operator<.
MSalters

22

De acordo com o padrão C ++ (operadores relacionais 5.9)

2 As conversões aritméticas usuais são realizadas em operandos do tipo aritmético ou de enumeração.

e

1 ... O tipo do resultado é booleano.

e (3.9.1 tipos fundamentais)

6 Os valores do tipo bool são verdadeiros ou falsos.49 [Nota: Não há tipos ou valores de bool assinados, não assinados, curtos ou longos. - end note] Os valores do tipo bool participam de promoções integrais (4.5).

e (4.5 Promoções integrais)

6 Um pré-valor do tipo bool pode ser convertido em um pré-valor do tipo int, com false se tornando zero e true se tornando um .

Portanto, em todos os seus exemplos, true é convertido em int 1 e false é convertido em int 0

Essas expressões

false < false
false < true
true < false
true < true

são inteiramente equivalentes a

0 < 0
0 < 1
1 < 0
1 < 1

8

Booleano falseé equivalente a int 0, e booleano trueé equivalente a int 1. Portanto, isso explica por que a expressão false < true=> 0 < 1é a única que retorna true.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.