14

Coming from that question: How to build a custom macro that behaves differently when used as constexpr (like assert)?

I wonder why it is possible to call a non constexpr function if it is conditional.

void bla( )
{
    std::cout << "bla called!" << std::endl;
}

constexpr bool check(bool condition)
{
    //bla(); // can not directly be called -> not constexpr! 
    condition ? void (0) : bla(); // compiles and runs even if condition is true or false!

    // if condition is const, it did not compile because it
    // directly force execution of non constexpr function
    true ? void(0): bla(); // also that compiles!, ok, directly evaluated
    //true ? bla(): void(0); // that did not compile;)

    //false ? void(0): bla(); // that did not compile;)
    false ? bla(): void(0); // compiles, ok, directly evaluated

    return 0;
}

int main()
{
    check( false );
    check( true );
}

Can someone explain which rules are given from the standard? As commented from W.F.: If result is used in constexpr term like an template parameter, it fails if condition results in evaluation of non constexpr function.

That makes assert to complain directly while compiling if result is used in constexpr term.

Klaus
  • 24,205
  • 7
  • 58
  • 113
  • This is just how constexpr works. – cpplearner Dec 09 '17 at 08:16
  • 1
    Shirt-circuit disables expression evaluation of `bla()`... This allows to put inside disabled expression nearly anything that isn't ill formed and has the same type as the other expression of ternary op. – W.F. Dec 09 '17 at 08:46
  • @W.F.: As coming from the assert macro, which expands to something which I wrote here, there is some kind of difference. The assert function will be called, if it is not constexpr. So there is still some kind of mystery in it. So if changing to `in_range( 7, 1, 5 )` I get a compile error, right! But the assert function will work. But why? – Klaus Dec 10 '17 at 08:32
  • Could you give an example? From what I understand you call in_range with non constexpr arguments. This still does not relax the constexpr condition given on function. – W.F. Dec 10 '17 at 09:12
  • @Klaus I think I got your point. Though it doesn't really surprise me - the constexpr function has to work in a constexpr manner for at least one set of constexpr arguments to be well formed just like in case of templates - you cannot create a template that has none viable specialization - it would be ill-formed with no diagnostic required... – W.F. Dec 10 '17 at 12:18
  • 1
    @W.F.: I simplified my example again and I got both passes to run. There is no short circuit and bla is executed if it is part of a conditional expression. So I wonder why that is allowed while direct call is not! – Klaus Dec 10 '17 at 19:37
  • this really look strange though when it comes to the real constexpr context it [does fail](https://wandbox.org/permlink/ddMwoUaAVtQWrCB9) as expected – W.F. Dec 10 '17 at 19:55
  • https://stackoverflow.com/a/66549687/15361975 Plz Check this out, will solves the problem – Asish_Johney Mar 09 '21 at 15:25

1 Answers1

10

A constexpr function implies that it is possible to evaluate the value of the function at compile time. Since this is possible for the input true the function is a valid constexpr. Remember that a constexpr function can have an address just as a regular function, it does not need to be compile time, only when used as a compile time function (which you do not in your example).

As mentioned on the constexpr page on cppreference:

A constexpr function must satisfy the following requirements:

  • it must not be virtual
  • its return type must be LiteralType
  • each of its parameters must be LiteralType
  • there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a core constant expression (for constructors, use in a constant initializer is sufficient) (since C++14). No diagnostic is required for a violation of this bullet. (Emphasis mine)

Your function fulfils all of the above requirements: it is not virtual, it returns a literal type, the parameter is literal. And more interestingly last bullet point: there exists at least one set of argument values for which the function is actually fully compile time. (hence my emphasis of the last bullet)

As W.F. mentioned in the comments, the function can be used compile time, but only for valid inputs, that is, inputs that does not lead to a sub expression that is not compile time constant. So the input true will work, but false will not since it will lead to bla being evaluated.

This is also stated in the standard: §10.1.5.5:

For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (8.20), or, for a constructor, a constant initializer for some object (6.6.2), the program is ill-formed, no diagnostic required.

constexpr int f(bool b)
{ return b ? throw 0 : 0; }   // OK

constexpr int f() 
{ return f(true); }           // ill-formed, no diagnostic required

See the examples from the standard document in particular.

Tommy Andersen
  • 7,165
  • 1
  • 31
  • 50