20

The following code is fine:

constexpr double square_cstxpr(double x) { return x * x; }

int main() {
    const int test = 5;
    constexpr double result = square_cstxpr((double)test);
}

However, if the type of test is changed from const int to const double, g++ gives the following error: the value of 'test' is not usable in a constant expression.

See the code and output of g++ here: http://coliru.stacked-crooked.com/a/2fe9b176c2b23798

Could somebody explain that behavior?

Georg
  • 1,078
  • 2
  • 9
  • 18
  • Please add the code that doesn't compile instead of describing it. – molbdnilo Aug 09 '17 at 14:12
  • 3
    @molbdnilo There is a link to coliru. – Zereges Aug 09 '17 at 14:13
  • Builds fine for me! – Geek Aug 09 '17 at 14:14
  • In fact, it is the working case scenario that puzzles me. Calling a `constexpr` function with a non-`constexpr` argument should be illegal, but for some reason g++ is happy when the argument's type is `const int` :/. – YSC Aug 09 '17 at 14:20
  • @Geek which compiler (and which version) do you use? – Georg Aug 09 '17 at 14:21
  • 1
    @YSC It is legal, see for example A Tour of C++ (by Bjarne Stroustrup), section 1.7. – Georg Aug 09 '17 at 14:23
  • @YSC I thnk [this](https://stackoverflow.com/a/370337/4342498) sheds light on it – NathanOliver Aug 09 '17 at 14:25
  • @NathanOliver I don't get it, how is it related to `const int` variables being implicitly `constexpr`? Maybe my wording was bad. The answer of Edgar on the other hand confirms what I suspected. – YSC Aug 09 '17 at 14:47

2 Answers2

12

Non-constexpr but const variables must be of integer or enumeration type for them to be usable in constant expressions. See [expr.const]/2:

an lvalue-to-rvalue conversion unless it is applied to

(2.7.1) a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or [..]

The reasons for this limitation must be mostly historical. Floating points have been handled with care when it comes to constant expressions; think non-type template parameters. This is due to their strongly platform dependent behaviour that renders compile time calculations less mathematical than they should be.

Columbo
  • 60,038
  • 8
  • 155
  • 203
12

From constant expression (Core constant expressions):

10) Any other lvalue-to-rvalue implicit conversion, unless the lvalue...

a) has integral or enumeration type and refers to a complete non-volatile const object, which is initialized with a constant expression

It means, that here:

const int test1 = 5;
constexpr double result1 = square_cstxpr((double)test1);

test1 is a constant expression, square_cstxpr can be called with test1 as an argument at compile time and its result can be assigned to constexpr variable result.

On the other hand, here:

const double test2 = 5;
constexpr double result2 = square_cstxpr((double)test2);

test2 is not a constant expression because it is not of integral or enumeration type. Consequently, square_cstxpr cannot be called at compile time with test2as an argument.

Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
  • Can you comment on why `const double` or `const float` not allowed in constant expression besides last statement of Columbo? – CKM Oct 04 '20 at 16:11