constexpr vs const vs constexpr const

2019-02-26 00:38发布

问题:

const-vs-constexpr-on-variables

What the guy says about constexpr is right if double is used (or float of course). However, if you change the var type from double to an integer type like int, char, etc, everything works. Why does that happen?

http://ideone.com/DAWABE

int main() 
{
    const int PI1 = 3;
    constexpr int PI2 = 3;
    constexpr int PI3 = PI1;  // works
    static_assert(PI1 == 3, "");  // works

    const double PI1__ = 3.0;
    constexpr double PI2__ = 3.0;
    constexpr double PI3__ = PI1__;  // error
    static_assert(PI1__ == 3.0, "");  // error
    return 0;
}

Update: the following line was a mistake, I meant PI3__ = PI1__

constexpr double PI3__ = PI1;  // I meant PI1__

Questions:

  1. Why const int = 3 is compile time constant but const double = 3.0 is not?

  2. Is there any reason why I should use constexpr const int val; over constexpr int val? They both seem to do exactly the same.

回答1:

From the comments it seems like OP is asking for Standard quote which defines const int as a compile-time constant, but const double as not.

The corresponding details are found in 5.19, Constant Expressions. In particular:

...an lvalue-to-rvalue conversion (4.1) unless it is applied to a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression...

int is an integral type, while double is not.



回答2:

Based on your answer in the comment this is my answer. The C++ standard makes it pretty clear. GCC 5.1 works pretty fine here though: https://godbolt.org/g/2oV6Hk

A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only § 5.20 134 c ISO/IEC N4567

[...]

(4.6) — integral promotions (4.5),

(4.7) — integral conversions (4.7) other than narrowing conversions (8.5.4),

[...]

For the reference for narrowing conversions (8.5.4/7) in n4567:

A narrowing conversion is an implicit conversion

  • from a floating-point type to an integer type, or
  • from long double to double or float, or from double to float, except where the source is a constant expression and the actual value after conversion is within the range of values that can be represented (even if it cannot be represented exactly), or
  • from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
  • from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.


回答3:

The compiler does not allow an implicit narrowing or non-integral promotion during the initialisation of a constexpr variable.

This will work:

int main()
{
    const int PI1 = 3;
    constexpr int PI2 = 3;
    constexpr int PI3 = PI1;  // works
    static_assert(PI1 == 3, "");  // works

    const double PI1__ = 3;
    constexpr double PI2__ = 3;
    constexpr double PI3__ = double(PI1);  // works with explicit cast
    static_assert(PI2__ == 3, "");  // works now. PI1__ isn't constexpr
    return 0;
}