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.
- A forma
cond ? a : be 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/else | Com 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
>=é… ocout(basic_ostreamé o tipo dele), e o direito é o7. Os desenhos~~~~e|embaixo mostram exatamente isso:std::cout << notade um lado,7do 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").
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.
- 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 umif):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ção | Use | Exemplo |
|---|---|---|
| Escolher entre dois valores | Ternário | qtd == 1 ? "item" : "itens" |
| Escolher entre duas ações | if/else | if (pagou) { entregar(); } else { cobrar(); } |
| Três ou mais opções por faixa | else if | Conceitos A, B, C, D |
| Maior/menor de dois números | std::max / std::min | std::max(a, b) |
Valor de uma const que depende de uma condição | Ternário | const 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.
-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
| Escrita | O que faz |
|---|---|
cond ? a : b | Vale 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 : c | Aninhado: funciona, mas prefira else if |
Exercícios
1. Complete o ternário
Complete para guardar "maior" ou "menor" de idade:
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 mostra1. - Os dois lados precisam ter um tipo comum: número e texto dá erro;
intedoubleviradouble;inteunsignedestraga os negativos. - Ternário aninhado funciona, mas é difícil de ler: prefira
else if.