mixing use of constexpr and const?

2019-02-21 19:33发布

问题:

I read a little of CLang implementation of standard library and it confuses me a little bit on const and constexpr.

template<class _Tp, _Tp __v>
struct integral_constant
{
    static constexpr _Tp value = __v;
};

template<class _Tp, _Tp __v>
const _Tp integral_constant<_Tp, __v>::value;

What makes me confusing is that, it is using constexpr inside class definition and const outside. My question is, is that allowed? And under what situation const and constexpr can be used interchangeably? Of course constexpr functions cannot apply to const, so I am talking about const data and constexpr data.

I did read some standard draft and the proposal in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf, but it makes me feel more confusing. So I have some more questions,

In N2235, it clearly states that, const data are not guaranteed to be a compile time constants, see the following example,

struct S {
    static const int size;
};
const int limit = 2 * S::size; // dynamic initialization
const int S::size = 256;

and constexpr is supposed to solve this, so at least under this situation, constexpr is not allowed as below,

struct S {
    static const int size;
};
constexpr int limit = 2 * S::size; // shall be error in my understanding
const int S::size = 256;

However, after reading C++ standard draft N3225, I see nowhere explicitly stated that the above example shall cause an error. Particularly, from 7.1.5/9,

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, the constructor shall be a constexpr constructor and every argument to the constructor shall be a constant expression. that call shall be a constant expression (5.19). Otherwise, every full-expression that appears in its initializer shall be a constant expression.

Therefore, if constexpr int limit = 2 * S::size; is invalid, then S::size must not be an constant expression, then from 5.19 (constant expression), I see nowhere the standard disallow 2 * S::size in the above example to not be a constant expression.

Can anybody point out anything I have overlooked? Thank you very much.

回答1:

S::size is not a constant expression according to N3225 §5.19p2:

A conditional-expression is a constant expression unless it involves one of the following…

  • 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
    • [other conditions that don't apply]

Note how the second bullet point I quoted allows an integral static data member which is itself initialized with a constant expression to also be a constant expression, but your S::size is uninitialized.

(Side-note: constant-expressions are defined in terms of conditional-expressions because that's how the C++ grammar works.)

If you're wondering how the lvalue-to-rvalue conversion happens, see §5p9:

Whenever a glvalue expression appears as an operand of an operator that expects a prvalue for that operand, the lvalue-to-rvalue (4.1), array-to-pointer (4.2), or function-to-pointer (4.3) standard conversions are applied to convert the expression to a prvalue.

This is probably a good example of how reading the standard doesn't make a good reference, though there's not much else available yet for 0x.



回答2:

"every full-expression that appears in it's initializer shall be a constant expression"

S::size is not a constant expression, therefore it can not appear in the initialization of a constant expression.