The reason for this ends up being to maintain C++03 compatibility since in C++03 const integral or const enumeration types initialized with a constant expression were usable in a constant expression but this was not the case for floating point.
The rationale for keeping the restriction can be found in defect report 1826 which came after C++11(this explains the ABI break comment) and asks (emphasis mine):
A const integer initialized with a constant can be used in constant expressions, but a const floating point variable initialized with a constant cannot. This was intentional, to be compatible with C++03 while encouraging the consistent use of constexpr. Some people have found this distinction to be surprising, however.
It was also observed that allowing const floating point variables as constant expressions would be an ABI-breaking change, since it would affect lambda capture.
One possibility might be to deprecate the use of const integral variables in constant expressions.
and the response was:
CWG felt that the current rules should not be changed and that programmers desiring floating point values to participate in constant expressions should use constexpr instead of const.
We can note that the question points out that allowing const floating point variables to be constant expression would be an ABI-break with respect to lambda capture.
This is the case since a lambda does not need to capture a variable if it is not odr-used and allowing const floating point variables to be constant expressions would allow them to fall under this exception.
This is because an lvalue-to-rvalue conversion of a const integer or enumeration type initialized with a constant expression or a constexpr literal type is allowed in a constant expression. No such exception exists for const floating point types initialized with a constant expression. This is covered in the draft C++11 standard section [expr.const]p2:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially
evaluated subexpression [...]
and includes in [expr.const]p2.9
- an lvalue-to-rvalue conversion (4.1) unless it is applied to
- a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized
with a constant expression, or
- a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an
object, or
Changing this would potentially effect the size of a lambda object if they no longer had to capture non-odr-used const floating point values which is an ABI break. This restriction was originally put in place to keep C++03 compatibility and to encourage the use of constexpr but now this restriction is in place it becomes hard to remove it.
Note, in C++03 we were only allowed to specify an in class constant-initializer for const integral or const enumeration types. In C++11 this was expanded and we were allowed to specify constant-initializer for constexpr literal types using a brace-or-equal-initializer.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…