C++ Operador Ternário

Muitas vezes, um if/else serve só para escolher entre dois valores: "Aprovada" ou "Recuperação", "item" ou "itens", "Bom dia" ou "Boa tarde". Escrever cinco linhas para isso é muito. O C++ tem um atalho para essa escolha: o operador ternário, condição ? valor_se_sim : valor_se_não. Nesta aula você vai usá-lo, descobrir por que ele precisa de parênteses dentro do cout e onde ele deixa de ajudar e passa a atrapalhar.

Você vai aprender
  • A forma cond ? a : b e como ela é lida.
  • A diferença entre o ternário (escolhe um valor) e o if (escolhe o que fazer).
  • Por que std::cout << nota >= 7 ? … : … não compila, e o erro silencioso dos parênteses no lugar errado.
  • O que acontece quando os dois lados têm tipos diferentes.
  • O ternário aninhado, e por que evitá-lo.

Todos os programas desta aula foram compilados com o g++ 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), com g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa.


A ideia: a pergunta com duas respostas prontas

Na lanchonete, a atendente pergunta: "Vai comer aqui? Se sim, bandeja; se não, saquinho". A pergunta já vem com as duas respostas. O ternário é isso, numa linha:

comer_aqui ? "bandeja" : "saquinho"

Leia o ? como "então" e o : como "senão": "comer aqui? então bandeja, senão saquinho". O nome "ternário" vem de três partes (a condição e os dois valores). É o único operador do C++ com três partes; por isso, muita gente o chama simplesmente de "o ternário".


1. A forma básica

#include <iostream>
#include <string>

int main() {
    double nota = 8.5;
    std::string situacao = (nota >= 7) ? "Aprovada" : "Recuperação";
    std::cout << situacao << "\n";
}
(nota >= 7)
A condição. Os parênteses aqui são opcionais, mas ajudam a enxergar onde ela termina.
?
"Então". Separa a condição dos valores.
"Aprovada"
O valor do ternário se a condição for verdadeira.
:
"Senão".
"Recuperação"
O valor se a condição for falsa.
std::string situacao = …
O ternário inteiro vira um valor, que aqui vai para a variável situacao.
$ g++ -std=c++23 -Wall -Wextra situacao.cpp -o situacao
$ ./situacao
Aprovada

Com if/else, o mesmo programa precisaria de mais linhas e de uma variável criada vazia antes:

Com if/elseCom o ternário
std::string situacao;
if (nota >= 7) {
    situacao = "Aprovada";
} else {
    situacao = "Recuperação";
}
std::string situacao =
    (nota >= 7) ? "Aprovada" : "Recuperação";

Ternário × if: valor × ação

A diferença fundamental: o if escolhe o que fazer (rodar um bloco ou outro); o ternário escolhe um valor. Por isso o ternário pode ficar no meio de uma conta, de uma atribuição ou de uma mensagem, onde um if não cabe.

#include <iostream>

int main() {
    int a = 12, b = 30;
    int maior = (a > b) ? a : b;
    std::cout << "Maior: " << maior << "\n";
}
$ ./maior
Maior: 30

(Para "o maior de dois", o C++ já tem std::max(a, b), visto em Matemática; o exemplo serve para mostrar a forma.)


2. Dentro do cout: parênteses obrigatórios

O uso mais comum do ternário é direto na mensagem. Por exemplo, para escrever "1 item" ou "3 itens":

#include <iostream>

int main() {
    int a = 1, b = 3;
    std::cout << a << (a == 1 ? " item" : " itens") << "\n";
    std::cout << b << (b == 1 ? " item" : " itens") << "\n";
}
$ ./plural
1 item
3 itens

Repare nos parênteses em volta do ternário inteiro. Eles não são enfeite. Veja o que acontece sem eles:

#include <iostream>

int main() {
    double nota = 8.5;
    std::cout << nota >= 7 ? "Aprovada" : "Recuperação";
    std::cout << "\n";
}
$ g++ -std=c++23 -Wall -Wextra semparen.cpp -o semparen
semparen.cpp: In function ‘int main()’:
semparen.cpp:5:23: error: no match for ‘operator>=’ (operand types are ‘std::basic_ostream<char>::__ostream_type’ {aka ‘std::basic_ostream<char>’} and ‘int’)
    5 |     std::cout << nota >= 7 ? "Aprovada" : "Recuperação";
      |     ~~~~~~~~~~~~~~~~~ ^~ ~
      |               |          |
      |               |          int
      |               std::basic_ostream<char>::__ostream_type {aka std::basic_ostream<char>}
semparen.cpp:5:23: note: there are 22 candidates
…mais de 160 linhas de "candidates"…
no match for ‘operator>=’
"Nenhum >= combina com estes tipos".
operand types are ‘std::basic_ostream<char>’ and ‘int’
O lado esquerdo do >= é… o cout (basic_ostream é o tipo dele), e o direito é o 7. Os desenhos ~~~~ e | embaixo mostram exatamente isso: std::cout << nota de um lado, 7 do outro.

Por quê? Porque o << tem prioridade maior que o >= e que o ?: (aula Precedência). O C++ leu a linha assim:

((std::cout << nota) >= 7) ? "Aprovada" : "Recuperação";

Ou seja: "mostre a nota; depois compare o cout com 7", o que não faz sentido. O editor do guia (Clang) dá um aviso mais direto antes do erro: warning: overloaded operator << has higher precedence than comparison operator [-Woverloaded-shift-op-parentheses] ("o operador << tem precedência maior que o de comparação").

Parênteses no lugar errado: compila, e mostra 1

Um conserto pela metade, pondo parênteses só na condição, é ainda pior, porque compila:

    std::cout << (nota >= 7) ? "Aprovada" : "Recuperação";
$ g++ -std=c++23 -Wall -Wextra meioparen.cpp -o meioparen
meioparen.cpp: In function ‘int main()’:
meioparen.cpp:5:32: warning: second operand of conditional expression has no effect [-Wunused-value]
    5 |     std::cout << (nota >= 7) ? "Aprovada" : "Recuperação";
      |                                ^~~~~~~~~~
meioparen.cpp:5:45: warning: third operand of conditional expression has no effect [-Wunused-value]
    5 |     std::cout << (nota >= 7) ? "Aprovada" : "Recuperação";
      |                                             ^~~~~~~~~~~~~
$ ./meioparen
1                        « mostrou o resultado da comparação (true = 1)

O C++ fez std::cout << (nota >= 7) (mostrou 1), e o ternário escolheu entre dois textos que ninguém usa. Os avisos dizem: "o segundo/terceiro operando da expressão condicional não tem efeito". Por isso a regra é: parênteses em volta do ternário inteiro.

#include <iostream>
#include <print>

int main() {
    double nota = 8.5;
    std::cout << (nota >= 7 ? "Aprovada" : "Recuperação") << "\n";
    std::println("{}", nota >= 7 ? "Aprovada" : "Recuperação");
}
$ ./certo
Aprovada
Aprovada

No std::println (aula std::format e std::print), o ternário é um argumento separado por vírgula, sem << por perto: não precisa de parênteses. Mesmo assim, muita gente os coloca, para ler melhor.

Missão: o programa escreve "1 maçãs". Use um ternário (entre parênteses!) para escrever maçã quando a quantidade for 1 e maçãs nos outros casos. A saída deve ser 1 maçã e 4 maçãs.

#include <iostream>

int main() {
    int a = 1, b = 4;
    std::cout << a << " maçãs\n";
    std::cout << b << " maçãs\n";
}
#include <iostream>

int main() {
    int a = 1, b = 4;
    std::cout << a << (a == 1 ? " maçã" : " maçãs") << "\n";
    std::cout << b << (b == 1 ? " maçã" : " maçãs") << "\n";
}

3. Os dois lados precisam combinar

O ternário vira um valor, de um tipo. Por isso, o C++ precisa achar um tipo comum para os dois lados.

Tipos que não combinam: erro

#include <iostream>

int main() {
    int qtd = 0;
    std::cout << (qtd > 0 ? qtd : "nenhum") << "\n";
}
$ g++ -std=c++23 -Wall -Wextra tipos2.cpp -o tipos2
tipos2.cpp: In function ‘int main()’:
tipos2.cpp:5:27: error: operands to ‘?:’ have different types ‘int’ and ‘const char*’
    5 |     std::cout << (qtd > 0 ? qtd : "nenhum") << "\n";
      |                   ~~~~~~~~^~~~~~~~~~~~~~~~

"Os operandos do ?: têm tipos diferentes: int e const char*" (const char* é o tipo de um texto entre aspas). Um número e um texto não têm tipo comum. Conserto: deixe os dois lados texto, por exemplo com std::to_string(qtd) (aula Texto ↔ Número), ou use um if.

Tipos numéricos diferentes: o C++ escolhe o "maior", sem avisar

#include <iostream>

int main() {
    bool inteiro = true;
    std::cout << (inteiro ? 7 : 2.5) / 2 << "\n";
}
$ ./tipos
3.5                      « 7 / 2 com inteiros daria 3

Um lado é int (7) e o outro é double (2.5). O tipo do ternário passa a ser double, mesmo quando o lado escolhido é o 7. Aí 7.0 / 2 dá 3.5, e não 3 (a divisão de inteiros, aula Operadores Aritméticos).

Pior ainda com unsigned (inteiro sem sinal, aula Inteiros):

#include <iostream>

int main() {
    bool devendo = true;
    unsigned int credito = 2;
    std::cout << (devendo ? -1 : credito) << "\n";
}
$ g++ -std=c++23 -Wall -Wextra tipos3.cpp -o tipos3
$ ./tipos3
4294967295               « o -1 virou unsigned: nenhum aviso!

O tipo comum de int e unsigned int é unsigned int, que não tem números negativos: o -1 "deu a volta" e virou o maior valor possível. Regra prática: deixe os dois lados do mesmo tipo.


4. Ternário aninhado: evite

Como o ternário é um valor, dá para colocar outro ternário no lugar do "senão" e fazer uma cadeia, como o else if:

#include <iostream>
#include <string>

int main() {
    double nota = 6.8;
    std::string conceito = nota >= 9 ? "A" : nota >= 7 ? "B" : nota >= 5 ? "C" : "D";
    std::cout << conceito << "\n";
}
$ ./aninhado
C

Funciona: o C++ lê de fora para dentro, "nota >= 9? então A, senão (nota >= 7? então B, senão (…))". Mas pare e tente achar onde está o conceito C sem contar os ? e :. Esse é o problema: é difícil de ler, de conferir a ordem das faixas e de mudar depois. Para mais de duas opções, use else if, que mostra cada faixa numa linha.

Quando o ternário vale a pena
  • Escolher entre dois valores simples: singular/plural, sim/não, mínimo/máximo.
  • Inicializar uma variável (especialmente uma const, que não pode ser preenchida depois por um if): const double taxa = vip ? 0.05 : 0.10;.
  • Quando a linha continua fácil de ler em voz alta.

Se o ternário precisa de quebras de linha, de outro ternário dentro ou faz alguma ação (como mostrar algo na tela), troque por if.


Formas diferentes + qual usar

SituaçãoUseExemplo
Escolher entre dois valoresTernárioqtd == 1 ? "item" : "itens"
Escolher entre duas açõesif/elseif (pagou) { entregar(); } else { cobrar(); }
Três ou mais opções por faixaelse ifConceitos A, B, C, D
Maior/menor de dois númerosstd::max / std::minstd::max(a, b)
Valor de uma const que depende de uma condiçãoTernárioconst int limite = vip ? 10 : 3;

Quando dá errado

error: no match for ‘operator>=’ (operand types are ‘std::basic_ostream<char>’ … and ‘int’)

O que significa: o ternário está dentro de um cout sem parênteses. O << "roubou" a variável da condição, e o C++ tentou comparar o próprio cout. Com <, a mensagem é a mesma com ‘operator<’; com ==, ‘operator==’.

Como consertar: std::cout << (cond ? a : b).

warning: second operand of conditional expression has no effect [-Wunused-value], e aparece 1 ou 0

O que significa: os parênteses estão só na condição. O cout mostrou a condição (1/0) e os dois valores foram jogados fora.

Como consertar: parênteses em volta do ternário inteiro, não só da condição.

error: operands to ‘?:’ have different types ‘int’ and ‘const char*’

O que significa: um lado é número e o outro, texto. O ternário precisa de um tipo comum.

Como consertar: converta um dos lados (std::to_string(qtd)) ou use if.

O número sai com vírgula, ou um -1 vira 4294967295

O que significa: os dois lados têm tipos numéricos diferentes, e o C++ converteu ambos para o "maior" (double, unsigned).

Como consertar: use o mesmo tipo dos dois lados (7.0 e 2.5; int e int).


Referência rápida

EscritaO que faz
cond ? a : bVale a se cond for verdadeira, senão b
x = cond ? a : b;Guarda o valor escolhido
std::cout << (cond ? a : b);Mostra o valor escolhido (parênteses obrigatórios)
std::println("{}", cond ? a : b);Mostra o valor escolhido (parênteses opcionais)
c1 ? a : c2 ? b : cAninhado: funciona, mas prefira else if

Exercícios

1. Complete o ternário

Complete para guardar "maior" ou "menor" de idade:

std::string s = idade >= 18 "maior" "menor";

2. Qual destas linhas mostra "par" ou "ímpar" corretamente?

3. Quanto vale (true ? 9 : 0.5) / 2?

4. Quando é melhor usar if/else em vez do ternário?

Missão final: a saudação

Missão: este programa deveria dizer "Bom dia" antes do meio-dia e "Boa tarde" depois, mas não compila. Rode, leia o erro e conserte com os parênteses no lugar certo. Com hora = 15, deve aparecer Boa tarde, Ana!; troque para 9 e confira o "Bom dia".

#include <iostream>

int main() {
    int hora = 15;
    std::cout << hora < 12 ? "Bom dia" : "Boa tarde" << ", Ana!\n";
}
#include <iostream>

int main() {
    int hora = 15;
    std::cout << (hora < 12 ? "Bom dia" : "Boa tarde") << ", Ana!\n";
}

Resumo

  • cond ? a : b: "cond? então a, senão b". O ternário inteiro vira um valor.
  • Use para escolher entre dois valores; para escolher entre ações, use if.
  • Dentro do cout, coloque parênteses em volta do ternário inteiro. Sem eles, erro (no match for ‘operator>=’); com parênteses só na condição, compila e mostra 1.
  • Os dois lados precisam ter um tipo comum: número e texto dá erro; int e double vira double; int e unsigned estraga os negativos.
  • Ternário aninhado funciona, mas é difícil de ler: prefira else if.