4

Is there some convenient way to write the expression

val = A ? A : B;

where A would only be evaluated once? Or is this the best (it is just ugly):

auto const& temp = A;
val = temp ? temp : B;

To clarify, A and B are not of type bool

Baruch
  • 20,590
  • 28
  • 126
  • 201

2 Answers2

5

Use the Elvis operator, which is supported in some C++ compilers:

val = A ?: B;

See Conditionals with Omitted Operands in gcc's documentation.

EDIT: This is not portable, and won't work in MSVC, for example. It works in gcc since 2.95.3 (March 2001), and on clang since 3.0.0.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Thomas Caissard
  • 846
  • 4
  • 10
  • 8
    This is not standard c++ though. – cigien Apr 22 '20 at 21:47
  • I agree but it is supported by most compilers. Your choice! – Thomas Caissard Apr 22 '20 at 21:51
  • Thankyou very much, uh huh uh – Stephen Duffy Apr 22 '20 at 21:54
  • I think this still evaluates to A?A:B its just short hand. – Stephen Duffy Apr 22 '20 at 21:55
  • 2
    @StephenDuffy It doesn't evaluate `A` twice – Artyer Apr 22 '20 at 21:57
  • @StephenDuffy actually, no. The point of this is so that the first operand is not evaluated twice. – cigien Apr 22 '20 at 21:57
  • 4
    @ThomasCaissard "*it is supported by most compilers*" - I've been using C++ for over 20 years and have never seen this operator before, or seen it used in any examples for any C++ compiler. Interesting. – Remy Lebeau Apr 22 '20 at 21:57
  • If you look at https://godbolt.org/z/Uc6-Tx, you can see that the elvis operator only call `doA()` once whereas the standard one calls it twice. – Thomas Caissard Apr 22 '20 at 21:58
  • @RemyLebeau msvc doesn't support it, so if you're targeting that, then you *can't* use it. – cigien Apr 22 '20 at 21:59
  • @RemyLebeau at least it works on gcc since 2.95.3 (Match 2001). It also works in clang. – Thomas Caissard Apr 22 '20 at 22:00
  • I've added a comment to state that this is not portable. Anyway for those who uses either gcc or clang it's a nice trick! – Thomas Caissard Apr 22 '20 at 22:03
  • 3
    @ThomasCaissard This "elvis" operator is a GCC extension, so of course GCC would support it, and CLang has GCC extensions, too. That is hardly "most compilers". – Remy Lebeau Apr 22 '20 at 22:09
  • 3
    Never seen that before. More importantly I would what it was if I saw it in real code (as I would suspect most people). Code that cause more WTF's per second than normal is not a good idea; in the long term somebody is going to say thats too quirky and replaced with something more standard, which will result in your expression being evaluated twice. If you want to gurantee that `A` is only evaluated "ONCE" then be explicit about that in the code (don't get too clever). – Martin York Apr 22 '20 at 22:49
2

Why not just

val = A || B;

?

That will make use of shortcutting to use A if it is true, otherwise B.

Note that this will only work if the values in question here are boolean; see the notes in the comments below from @ApproachingDarknessFish.

For non-booleans, if you want standard C++ then you will probably have to use your suggested ugly option.

lxop
  • 7,596
  • 3
  • 27
  • 42
  • 1
    In C++, this only works if `A` and `B` are boolean values. Because the result will always have boolean type. – Brian Bi Apr 22 '20 at 21:43
  • I assume `A` is not `bool`, but it is convertible to `bool`. I don't think it will work then. – CygnusX1 Apr 22 '20 at 21:43
  • 2
    Confirmed. While this is a common idiom in many scripting languages, it will only satisfy what the OP is looking for if `A` and `B` both have type `bool`. If they are primitive types that are not `bool` but are convertible to `bool`, then the result will be coerced to type `bool`. If they are more complex types that overload `operator ||`, then [the evaluation will not short-circuit](https://stackoverflow.com/a/628554/1530508). – ApproachingDarknessFish Apr 22 '20 at 21:48
  • True, this will only work properly for boolean values. I'll update the answer a little – lxop Apr 22 '20 at 21:49
  • Gosh, there's a lot of hate for this answer without any related comments - note that there's nothing in the question that says the values aren't booleans, and please be decent enough to leave a comment when downvoting – lxop Apr 22 '20 at 22:23
  • This is a great idea. It works if A and B are calls to function returning std::optional. If A is not empty, B won't be called. I found Elvis. – reder Oct 12 '20 at 20:06