Problem
Deferred operator&& and operator|| currently use the generic invocation machinery:
return invoke(std::logical_and<>{}, lhs, rhs);
The generic expression evaluator evaluates all stored operands before calling the function object. As a result, the right operand is evaluated even when native C++ would short-circuit it.
The interface visually promises normal C++ behavior:
condition && side_effect();
condition || side_effect();
Eager evaluation can cause unexpected side effects, invalid accesses, or unnecessary work.
Expected behavior
lhs && rhs: evaluate rhs only if lhs evaluates to true.
lhs || rhs: evaluate rhs only if lhs evaluates to false.
- Evaluate
lhs exactly once.
- Preserve deferred evaluation: neither operand is evaluated while constructing the expression.
Proposed implementation direction
Introduce dedicated logical expression nodes or dedicated lazy function objects that receive deferred operands rather than already-evaluated values. A generic expression_ using the current apply() path cannot provide short-circuiting because operand evaluation has already occurred.
The dedicated node should:
- evaluate the left subexpression;
- conditionally evaluate the right subexpression;
- return a result consistent with built-in logical operators;
- expose both operands through
subexpression_types and visit().
Design considerations
- Preserve contextual conversion to
bool as closely as practical.
- Match native evaluation order and evaluate each selected operand once.
- Determine the result type and avoid returning operand types as built-in
&&/|| return bool.
- Maintain
constexpr support.
- Ensure const and non-const expressions behave consistently.
- Consider whether any other operator currently appears to promise native sequencing semantics but uses eager generic evaluation.
- Document that overloaded deferred operators build expressions immediately but execute with native logical short-circuit behavior later.
Acceptance criteria
Problem
Deferred
operator&&andoperator||currently use the generic invocation machinery:return invoke(std::logical_and<>{}, lhs, rhs);The generic expression evaluator evaluates all stored operands before calling the function object. As a result, the right operand is evaluated even when native C++ would short-circuit it.
The interface visually promises normal C++ behavior:
Eager evaluation can cause unexpected side effects, invalid accesses, or unnecessary work.
Expected behavior
lhs && rhs: evaluaterhsonly iflhsevaluates to true.lhs || rhs: evaluaterhsonly iflhsevaluates to false.lhsexactly once.Proposed implementation direction
Introduce dedicated logical expression nodes or dedicated lazy function objects that receive deferred operands rather than already-evaluated values. A generic
expression_using the currentapply()path cannot provide short-circuiting because operand evaluation has already occurred.The dedicated node should:
subexpression_typesandvisit().Design considerations
boolas closely as practical.&&/||returnbool.constexprsupport.Acceptance criteria
&&skips the right operand when the left operand is false.||skips the right operand when the left operand is true.constexprcases are tested.