1

I often come across this situation:

if (A && B)
{
    C();
}
else
{
    D();
}

But when A is constexpr, there is no way to indicate that to the if statement if B is not also constexpr. Further, the workaround for enabling use of constexpr is ugly:

if constexpr (A)
{
    if (B)
    {
        C();
    }
    else
    {
        D();
    }
}
else
{
    D();
}

Is there a proposal to make constexpr if statements more flexible ie. to consider which of the conditionals are constexpr? And if not, is there a workaround for this situation other than assuming the compiler will do the right thing (which they don't always do).

metamorphosis
  • 1,972
  • 16
  • 25
  • Dispatch via function pointers maybe? – πάντα ῥεῖ Sep 08 '20 at 23:48
  • 2
    Can you give an example of where you think the compiler does the wrong thing? – cigien Sep 08 '20 at 23:48
  • 4
    No, there is no proposal. And there is no workaround. And I doubt that it's likely there will ever be. Due to the fundamentally different semantics of `if constexpr`, it is unwise to make something like this be derived indirectly; and it's something that should always be explicitly indicated. – Sam Varshavchik Sep 08 '20 at 23:48
  • 2
    Why so pessimistic about compilers, if A is constexpr, then even without `if constexpr`, the optimised assembly won't look much different. – Mansoor Sep 09 '20 at 00:18
  • @Mansoor, no, that's not the case in all cases. – metamorphosis Sep 09 '20 at 07:42
  • @SamVarshavchik, The rules for constexpr functions in C++20 allow for flexibility of calling between runtime and compiletime. I see no reason why this would not be implemented in the future, being familiar with the standards committee. – metamorphosis Sep 09 '20 at 07:42
  • @cigien it is pretty common for compilers of various types (MSVC, GCC) to not compile-time evaluate code which's not explicitly marked as constexpr. I've done the before-and-after tests, seen the file size reduction and don't personally feel a need to provide examples. But if you want specific examples, earlier versions of GCC did not optimize out is_trivially_destructible calls- – metamorphosis Sep 09 '20 at 07:43
  • 1
    @metamorphosis: "*The rules for constexpr functions in C++20 allow for flexibility of calling between runtime and compiletime.*" But functions are the *only* place where such flexibility exists. In every other place where `constexpr` is allowed, the standard *means it*. `if constexpr`, `constexpr` variables, etc. The oddball out is functions, which is why C++20 gave us `consteval` functions. – Nicol Bolas Sep 11 '20 at 00:00
  • @NicolBolas Yes. well aware of that, thank you. – metamorphosis Sep 13 '20 at 02:23

2 Answers2

3

if constexpr is not "faster if". It's not the if statement you use when a conditional expression happens to be a constant expression. It's not even the if statement you use when you want the compiler to test the condition at compile time (compilers are capable of doing that on their own).

The meaning of the if constexpr usage in your second example is that the expression C() is not meant to be valid C++ unless the condition represented by A is true. This is why you guard a code block with if constexpr; this is why the feature was added to the language to begin with. Obviously you can use it for other things, but if all you care about is getting the compiler to evaluate a condition expression at compile-time, you shouldn't be using if constexpr.

And it is for this reason why what you're asking for is not forthcoming.

Looking at your second example, the difficulties in replicating if constexpr's behavior across an expression that is only partially a constant expression become apparent. The rules would need a fair bit of complexity. In your example, D() must be valid C++ code even if A is true, since B could be false at runtime.

You'd need to build some fairly complex rules about how such "partial constexpr" expressions prohibit the evaluation of the various code branches. And that can easily lead to difficulty in users understanding when a complex expression will cull out which branches and when it won't.

Better to just make users write it out long-form in these cases.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • The problem is that compilers aren't capable of evaluating which statements are compile-time and which aren't. Ideally they would be, but they don't always. See my responses to the comments on the question. I take your point that code which isn't accessible due to constexpr A being false would not be evaluated at compile-time. However that doesn't affect the situation I'm describing - both || and && are easily usable in this situation. && means if A is false, the expression is false and isn't evaluated. || means if A is false it becomes a runtime 'if' based on B, otherwise compile-time. – metamorphosis Sep 09 '20 at 09:49
  • 1
    @metamorphosis: "*The problem is that compilers aren't capable of evaluating which statements are compile-time and which aren't.*" Capable is the wrong word; a compiler has all of the knowledge it needs to do it. Choosing not to is not a lack of capability. In any case, this is too much of a micro-optimization to bother writing a whole language feature for. In the few instances where a compiler fails to optimize a constexpr condition away in hot code, you can detect this and restructure your code accordingly. But only after your profiler tells you that you need those cycles in that location. – Nicol Bolas Sep 09 '20 at 13:33
  • @metamorphosis: "*I'm not appreciating the level of angst I'm getting from your writing.*" I'm not exhibiting any "angst", so I don't know where that's even coming into play. And yes, I read your comments, and my point still applies. From your comments, you care about this because "it is pretty common for compilers of various types (MSVC, GCC) to not compile-time evaluate code which's not explicitly marked as constexpr". The only reason to care about this is *performance*, and my point is that this is a micro-optimization for which a dedicated feature is not warranted. – Nicol Bolas Sep 10 '20 at 23:58
  • Yeah whatever mate – metamorphosis Sep 13 '20 at 02:19
0

is there a workaround for this situation other than assuming the compiler will do the right thing (which they don't always do).

Well... when the code in your example is all the content of a void function (or a final part of a void function)... what about something as follows?

if constexpr ( A )
   if ( B )
    {
      C();

      return;
    }

D();

Otherwise (if your code is a not-final fragment of a bigger function), I suppose you can wrap it in a while

while ( 1 )
 {
   if constexpr ( A )
      if ( B )
       {
         C();

         break;
       }

   D();

   break;
 }

but I suppose this is a "assuming the compiler will do the right thing" case.

max66
  • 65,235
  • 10
  • 71
  • 111