My Favorite Ternary Operation

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)

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();
   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

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.