The conditional operator

Operator Symbol Form Meaning
Conditional ?: condition ? a : b If condition is true, evaluate a; otherwise evaluate b

The conditional operator (?:) (sometimes called the arithmetic if operator) is a ternary operator—it takes three operands. Since it's historically been C++'s only ternary operator, it's often called simply "the ternary operator."

The ?: operator provides a compact way to write certain if-else statements.

Related content: We cover if-else statements in the Introduction to if statements lesson.

As a refresher, an if-else statement looks like:

if (condition)
    statement1;
else
    statement2;

If condition is true, statement1 executes; otherwise, statement2 executes. The else clause and statement2 are optional.

The ?: operator has this form:

condition ? expression1 : expression2;

If condition is true, expression1 evaluates; otherwise, expression2 evaluates. Unlike if-else, the : and expression2 are required.

Consider an if-else statement like:

if (a > b)
    largest = a;
else
    largest = b;

This can be rewritten as:

largest = ((a > b) ? a : b);

In such cases, the conditional operator makes code more compact without sacrificing readability.

An example

#include <iostream>

int getNumber()
{
    std::cout << "Enter a number: ";
    int num{};
    std::cin >> num;
    return num;
}

int main()
{
    int first{getNumber()};
    int second{getNumber()};
    int larger{(first > second) ? first : second};
    std::cout << "The larger of " << first << " and " << second << " is " << larger << ".\n";

    return 0;
}

Let's enter 8 and 3 (so first is 8 and second is 3). When initializing larger, the expression (8 > 3) ? 8 : 3 evaluates. Since 8 > 3 is true, we get true ? 8 : 3, which evaluates to 8. The program prints:

The larger of 8 and 3 is 8.

Now let's enter 3 and 8 (so first is 3 and second is 8). We get (3 > 8) ? 3 : 8, which is false ? 3 : 8, evaluating to 8. The program prints:

The larger of 3 and 8 is 8.

The conditional operator evaluates as an expression

The conditional operator evaluates as part of an expression, so it works anywhere expressions are accepted. When operands are constant expressions, the entire conditional operation can be a constant expression.

This allows the conditional operator in places where statements cannot appear—for instance, when initializing variables:

#include <iostream>

int main()
{
    constexpr bool isWeekend{false};
    constexpr int sleepHours{isWeekend ? 9 : 7};
    std::cout << "You get " << sleepHours << " hours of sleep.\n";

    return 0;
}

There's no straightforward if-else replacement for this.

You might try:

#include <iostream>

int main()
{
    constexpr bool isWeekend{false};

    if (isWeekend)
        constexpr int sleepHours{9};
    else
        constexpr int sleepHours{7};

    std::cout << "You get " << sleepHours << " hours of sleep.\n"; // compile error: sleepHours undefined

    return 0;
}

This won't compile because sleepHours doesn't exist outside its if/else scope. Variables defined inside if-statements or else-statements are destroyed when those blocks end.

To use if-else, you'd need something like:

#include <iostream>

int getSleepHours(bool isWeekend)
{
    if (isWeekend)
        return 9;
    else
        return 7;
}

int main()
{
    const int sleepHours{getSleepHours(false)};
    std::cout << "You get " << sleepHours << " hours of sleep.\n";

    return 0;
}

This works because getSleepHours(false) is an expression, with the if-else logic inside a function. But this is considerably more code than simply using the conditional operator.

Parenthesizing the conditional operator

C++ evaluates most operators before the conditional operator, making it easy to write expressions that don't evaluate as expected.

Related content: We cover operator evaluation priority in the Operator precedence and associativity lesson.

Example:

#include <iostream>

int main()
{
    int temperature{20};
    int threshold{15};
    int adjustment{5 - temperature > threshold ? temperature : threshold};
    std::cout << adjustment;

    return 0;
}

You might expect this to evaluate as 5 - (temperature > threshold ? temperature : threshold) (yielding -10), but it actually evaluates as (5 - temperature) > threshold ? temperature : threshold (yielding 20).

Here's another common mistake:

#include <iostream>

int main()
{
    int age{20};
    std::cout << (age >= 18) ? "adult" : "minor";

    return 0;
}

You'd expect this to print adult, but it prints 1.

Advanced note: Here's what happens: age >= 18 evaluates to true. The expression becomes std::cout << true ? "adult" : "minor". Since operator<< has higher precedence than operator?:, this evaluates as if written (std::cout << true) ? "adult" : "minor". Thus std::cout << true executes, printing 1 (and returning std::cout). The partially evaluated expression is now std::cout ? "adult" : "minor". The compiler converts std::cout to bool, which likely returns false. Assuming it's false, we have false ? "adult" : "minor", evaluating to "minor". Our fully evaluated statement is just "minor";—an expression statement with a string literal that has no effect.

To avoid evaluation issues, parenthesize the conditional operator as follows:

  • Parenthesize the entire conditional operation (including operands) when used in compound expressions
  • For readability, consider parenthesizing the condition if it contains operators (excluding function call operators)

The conditional operator's operands don't need parentheses.

Examples of proper parenthesization:

return isStunned ? 0 : movesLeft;              // not in compound expression, no operators in condition
int larger{(a > b) ? a : b};                   // not in compound expression, condition has operators
std::cout << (isOnline() ? "connected" : "offline"); // compound expression, condition has no operators (function call excluded)
std::cout << ((temperature > threshold) ? temperature : threshold); // compound expression, condition has operators
Best Practice
Parenthesize the entire conditional operation (including operands) when used in compound expressions. For readability, consider parenthesizing the condition if it contains operators (excluding function call operators).

The operand types must match or be convertible

To satisfy C++'s type checking, one of the following must be true:

  • The second and third operands must have matching types
  • The compiler must be able to convert one or both operands to matching types (conversion rules are complex and may yield surprising results)

Advanced note: Alternatively, one or both of the second and third operands can be throw expressions. We cover throw in the Basic exception handling lesson.

Examples:

#include <iostream>

int main()
{
    std::cout << (true ? 10 : 20) << '\n';      // okay: both operands are int

    std::cout << (false ? 10 : 2.5) << '\n';    // okay: int 10 converts to double

    std::cout << (true ? -10 : 20u) << '\n';    // surprising: -10 converts to unsigned, out of range

    return 0;
}

Assuming 4-byte integers, this prints:

10
2.5
4294967286

Mixing fundamental types is generally fine (except mixing signed and unsigned values). If either operand isn't a fundamental type, explicitly convert one or both operands yourself to ensure predictable results.

Related content: The surprising signed/unsigned case results from arithmetic conversion rules, covered in the Arithmetic conversions lesson.

If the compiler can't find a way to convert operands to matching types, a compile error results:

#include <iostream>

int main()
{
    constexpr int value{10};
    std::cout << ((value != 10) ? value : "value is 10"); // compile error: can't match int and C-style string literal

    return 0;
}

One operand is an integer, the other is a C-style string literal. The compiler can't find matching types, causing a compile error.

You can explicitly convert types or use if-else:

#include <iostream>
#include <string>

int main()
{
    int value{10}; // intentionally non-constexpr for this example

    // explicit conversion
    std::cout << ((value != 10) ? std::to_string(value) : std::string{"value is 10"}) << '\n';

    // or use if-else
    if (value != 10)
        std::cout << value << '\n';
    else
        std::cout << "value is 10" << '\n';

    return 0;
}

Advanced note: If value were constexpr, value != 10 would be a constant expression. In such cases, use if constexpr instead of if, or your compiler may generate a warning (promoted to an error if treating warnings as errors). Since we haven't covered if constexpr yet (see the Constexpr if statements lesson), value is non-constexpr in this example to avoid warnings.

When should you use the conditional operator?

The conditional operator is most useful when:

  • Initializing an object with one of two values
  • Assigning one of two values to an object
  • Passing one of two values to a function
  • Returning one of two values from a function
  • Printing one of two values

Complex expressions should generally avoid the conditional operator—they tend to be error-prone and hard to read.

Best Practice
Avoid the conditional operator in complicated expressions.

Summary

  • Conditional operator (?:): Also called the ternary operator; evaluates condition ? expression1 : expression2
  • How it works: If the condition is true, evaluates and returns expression1; otherwise, evaluates and returns expression2
  • Expression, not statement: The conditional operator produces a value and can be used in contexts where statements cannot (e.g., variable initialization)
  • Compact alternative to if-else: Useful for simple cases like int larger{(a > b) ? a : b} instead of multi-line if-else
  • Precedence: Most operators evaluate before the conditional operator, making it easy to write incorrect expressions
  • Parenthesization: Always parenthesize the entire conditional operation when used in compound expressions; consider parenthesizing the condition if it contains operators
  • Type matching: The second and third operands must have matching types or be convertible to matching types
  • Type conversion surprises: Mixing signed and unsigned integers can produce unexpected results due to arithmetic conversion rules
  • Best use cases: Initializing variables, assigning values, passing function arguments, returning values, or printing one of two values
  • Avoid complexity: Don't use the conditional operator in complicated expressions—use if-else instead for clarity

The conditional operator is a powerful tool for writing concise code, but it requires careful parenthesization and understanding of type conversion rules. Use it for simple cases where it improves readability, but fall back to if-else statements when expressions become complex or involve incompatible types.