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
Martian
Alien
Outkast
?