OpenSees Cloud

OpenSees AMI

My Favorite Ternary Operation

Original Post - 19 Mar 2023 - Michael H. Scott

Visit Structural Analysis Is Simple on Substack.


Most native C++ operations are binary, taking two arguments, e.g., a + b, or unary, taking one argument, e.g., a++. But C++ (and many other languages) has a native “conditional operator”, which is ternary, taking three arguments. Known simply as ?:, the conditional operator has the following syntax:

(condition) ? true_outcome : false_outcome;

The conditional operator is shorthand for an if/else statement

if (condition)
   true_outcome;
else
   false_outcome;

Python also has a conditional operator, but it uses an odd ordering where the condition is the second argument even though it’s somehow evaluated first.

true_outcome if condition else false_outcome

Anyway, I’ve always seen the conditional operator as something you could do if you wanted to save a couple lines of code and earn some C++ XP. A common use case you’ll see throughout OpenSees is simple min or max logic, e.g., in HystereticMaterial.cpp the variable rotrel is set to the smaller of rotlim and TrotPu.

double rotrel = (rotlim < TrotPu) ? rotlim : TrotPu;

Then I came across a situation where the conditional operator was actually necessary.

In PR #958, I combined the computations of basic stiffness and initial basic stiffness into one function in each of the dispBeamColumn and timoshenkoBeamColumn elements. The computations only differ by which method you call on the section objects–either getSectionTangent() or getInitialTangent().

To avoid an implicit call to a copy constructor, the previous implementation used a constant reference to a Matrix object.

const Matrix &ks = sections[i]->getSectionTangent();

With the new implementation, I wanted to use a boolean variable, initial, to indicate whether ks should refer to the tangent stiffness or the initial stiffness. But C++ won’t let you declare an uninitialized reference, i.e., you cannot do something like this:

const Matrix &ks; // THIS IS NOT ALLOWED IN C++
if (initial)
   ks = sections[i]->getInitialTangent();
else
   ks = sections[i]->getSectionTangent();

Yeah, I could have defined ks as a Matrix object instead of a reference, but that would incur copy constructor overhead. Or I could have done some weird stuff with pointers.

Instead, I employed the ?: operator to initialize the reference.

const Matrix &ks = (initial) ? theSections[i]->getInitialTangent() : theSections[i]->getSectionTangent();

Done. Worked like a charm!



My Favorite Ternary Operation

My Favorite Martian

Martian

Alien

ATLiens

Outkast

Stankonia

?

Outkast ?