Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The ?: operator, formally known as the conditional operator, is the only ternary operator in C++. It evaluates a boolean condition and executes exactly one of two subsequent expressions based on whether the condition resolves to true or false.
condition ? expression_if_true : expression_if_false

Evaluation Mechanics

  1. Contextual Conversion: The condition is contextually converted to bool.
  2. Sequence Point: There is a strict sequencing guarantee. The evaluation of the condition is sequenced before the evaluation of either result expression. All side effects of the condition are guaranteed to be committed before the selected expression is evaluated.
  3. Short-Circuiting: The operator guarantees short-circuit evaluation. If the condition is true, only expression_if_true is evaluated. If false, only expression_if_false is evaluated. The unselected expression is entirely ignored, meaning any side effects or function calls within it will not occur.

Type Resolution

Because the conditional operator is an expression, it must yield a single, well-defined type at compile time, regardless of runtime evaluation. The compiler determines the result type by analyzing expression_if_true and expression_if_false:
  • If both expressions have the exact same type, the result is of that type.
  • If both expressions are of type void (e.g., calling two void-returning functions), the result type of the ternary expression is validly void.
  • If one of the expressions is a throw expression, the result type of the ternary operation is strictly the type of the non-throw expression.
  • If the types differ, the compiler attempts to find a common type:
    • Arithmetic Types: The compiler applies the usual arithmetic conversions to determine the common type (e.g., if one operand is int and the other is double, the result type is double).
    • Class Hierarchies: If one operand is of a Base class and the other is of a Derived class, the compiler evaluates reference binding rules to determine if a common base type can be targeted.
    • Implicit Conversions: If an implicit conversion exists from one type to the other in exactly one direction, the result takes the target type.
    • Ambiguity/Mismatch: If implicit conversions exist in both directions between two distinct class types, or if neither type can be implicitly converted to the other, the compiler generates an error.

Value Categories (Lvalue, Xvalue, Prvalue)

The C++ conditional operator determines the value category of its result based on the value categories and types of its operands:
  • Lvalue Result:
    • If both expression_if_true and expression_if_false are lvalues of the exact same type, the result of the entire ternary expression is an lvalue.
    • If one operand is an lvalue of a Base class and the other is an lvalue of a Derived class, the Derived object can be implicitly converted to a Base&. The result is an lvalue of the Base class.
// Syntactically valid if both operands yield lvalues of a compatible type (condition ? a : b) = value;
* **Xvalue Result:** If both expressions are xvalues of the exact same type, the result is an xvalue.
  ```cpp
// Yields an xvalue, allowing move semantics
condition ? std::move(a) : std::move(b)
  • Prvalue Result:
    • If the operands are glvalues of the same type but have different value categories (e.g., one lvalue and one xvalue), the result is a prvalue. The compiler forces a copy or move to materialize a new temporary object because a single expression cannot dynamically resolve to different reference types.
    • If the expressions have different types requiring conversion to a common type (excluding the Base/Derived lvalue reference binding mentioned above), or if at least one of the expressions is a prvalue, the result is a prvalue.

Precedence and Associativity

  • Precedence: The conditional operator has very low precedence. It shares the exact same precedence level as assignment operators (=, +=, etc.) and the throw operator. Because it binds loosely, it often requires parentheses when embedded within larger expressions (such as stream insertion <<).
  • Associativity: It is right-to-left associative. When multiple conditional, assignment, or throw operators are chained, they group from the right.
a ? b : c ? d : e
Due to right-to-left associativity, the compiler parses the above chain as:
a ? b : (c ? d : e)
Master C++ with Deep Grasping Methodology!Learn More