26

GCC9 already implements std::is_constant_evaluated. I played a little bit with it, and I realized it is somewhat tricky. Here’s my test:

constexpr int Fn1()
{
  if constexpr (std::is_constant_evaluated())
    return 0;
  else
    return 1;
}

constexpr int Fn2()
{
  if (std::is_constant_evaluated())
    return 0;
  else
    return 1;
}

int main()
{
  constexpr int test1 = Fn1(); // Evaluates to 0
  int test2 = Fn1();           // Evaluates to 0
  int const test3 = Fn1();     // Evaluates to 0

  constexpr int test4 = Fn2(); // Evaluates to 0
  int test5 = Fn2();           // Evaluates to 1
  int const test6 = Fn2();     // Evaluates to 0
}

According to these results, I extracted the following conclusions:

  • if constexpr (std::is_constant_evaluated()) always evaluates the true branch. Therefore, it makes no sense to use this construct.

  • If the compiler evaluates a variable at compile time, std::is_constant_evaluated()) is true, no matter whether that variable is explicitly annotated constexpr or not.

Am I right?

Barry
  • 286,269
  • 29
  • 621
  • 977
metalfox
  • 6,301
  • 1
  • 21
  • 43

2 Answers2

21

if constexpr requires a constant expression for a condition. So is_constant_evaluated is of course always going to be true in such a context.

It's meant for a regular if. The purpose is to not go into a code path that is illegal in a constexpr function when evaluated in a constant expression. But to let it execute in runtime. It's not there to eliminate those code paths from the function altogether.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • there can be code paths in a `constexpr` function that cannot be evaluated at compile-time? There is so much I still have to learn :) – 463035818_is_not_an_ai Jan 18 '19 at 10:14
  • 4
    @user463035818 `constexpr` means "there exist arguments to this function that may allow compile-time evaluation". It does not mean "this function will always be evaluated at compile-time". This means e.g. `std::abs` can be `constexpr`: When called with `constexpr` arguments it can be evaluated at compile-time, but it can still be used as normal at runtime. – Max Langhof Jan 18 '19 at 10:16
  • @MaxLanghof but can you actually implement a `constexpr` function with logic that can never be executed at compile time? I think that's what **user463035818** meant. I am surprised too. The thing for function template is obvoisly different. Example which I think is relevant - you can never have a `constexpr` function that will return `std::string`, no matter where and how you'd call it. – Fureeish Jan 18 '19 at 10:20
  • @MaxLanghof ah thanks, got it. In case you pass non compile time arguments it makes little sense to require that the function can be evaluated at compile time – 463035818_is_not_an_ai Jan 18 '19 at 10:20
  • I would have expected the compiler to optimize away the constant evaluation code path when generating the runtime version of the function, which is something likely to happen anyway. So `if constexpr` made sense to me. – metalfox Jan 18 '19 at 10:29
  • @metalfox - I understand. It's a bit counter intuitive at first. Point is. The function tells you *when* something is evaluated. So if you put it in a context that can only be evaluated at a specific time.. – StoryTeller - Unslander Monica Jan 18 '19 at 10:33
18

Here's how I think about this, maybe you'll find this helpful... maybe not. Note that I think writing if constexpr (std::is_constant_evaluated()) will be a really common error, and it's an easy trap to fall into. But hopefully compilers will just diagnose that case. Submitted 91428, which is fixed for gcc 10.1.


We essentially have two different rules for code - the typical rules for normal runtime code, and the restrictions for constant expressions that are for constexpr programming. Those are the expr.const restrictions: no UB, no reinterpret_cast, etc. Those restrictions keep decreasing from language standard to language standard, which is great.

Basically, control flow (from a code path perspective) alternates between being in the "full runtime" mode and the constexpr mode. Once we enter the constexpr mode (whether by initializing a constexpr object or evaluating a template parameter or ...), we stay there until we're done... and then we're back to full runtime mode.

What is_constant_evaluated() does is simply: Am I in constexpr mode? It tells you if you're on a context that requires constant expressions.

In that view, let's look at if constexpr (is_constant_evaluated()). Regardless of what state we used to be in, if constexpr requires a constant expression as its initialized so this lifts us into constexpr mode if we weren't there already. Hence, is_constant_evaluated() is just true - unconditionally.

However, for if (is_constant_evaluated()), a simple if doesn't change our state between runtime and constexpr. So the value here depends on the context it was called from. Initializing test4 puts us onto constexpr mode because it's a constexpr object. For the duration of its initialization, we follow the constant expression rules... so is_constant_evaluated()is true. But once we're done, we're back to runtime rules... so in the initialization of test5, is_constant_evaluated() is false. (And then test6 is an unfortunate language special case - you can use constant integral variables as constant expressions, so we treat their initialization the same way for these purposes.)

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thanks for the detailed explanation. I'm fine with the `test6` case. It is quite logical to behave that way. I find the `if constexpr` case more counter-intuitive. – metalfox Jan 18 '19 at 15:54
  • 4
    @metalfox I suspect that will be a very common misunderstanding. I will add a note to the answer to that effect – Barry Jan 18 '19 at 16:03
  • 2
    GCC 10+ will warn about the `if constexpr` case: https://gcc.gnu.org/ml/gcc-patches/2019-08/msg01841.html – metalfox Aug 28 '19 at 13:41
  • The case with constant integers is even weirder, since they *can* be constant expressions, but needn’t be. (Of course, any dynamic initialization can be done statically, but that’s usually unobservable.) – Davis Herring Aug 29 '19 at 02:19
  • GCC 91428 says that is_constant_evaluated is probably going to be removed. What will it be replaced by? – Johannes Schaub - litb Dec 28 '19 at 21:56
  • @Johannes It's not getting removed. – Barry Dec 28 '19 at 22:00