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;
}
duncan
fonte
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
Shafik Yaghmour
fonte
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.

Mark Ransom
fonte
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
Vlad de Moscou
fonte
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.

Blindstealer
fonte