Смешивание использования constexpr и const?

Я немного читал о реализации стандартной библиотеки CLang, и это немного смущает меня в const и 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;

Что меня пугает, так это то, что он использует определение constexpr внутри класса и const. Мой вопрос в том, что это разрешено? И при какой ситуации const и constexpr можно меня использовать взаимозаменяемо? Конечно, функции constexpr не могут применяться к const, поэтому я говорю о данных const и constexpr.

Я прочитал некоторый стандартный проект и предложение в http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf, но это заставляет меня чувствовать себя более запутанным. Поэтому у меня есть еще несколько вопросов,

В N2235 он четко заявляет, что константные данные не гарантируются как константы времени компиляции, см. следующий пример,

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

и constexpr должен решить это, поэтому, по крайней мере, в этой ситуации constexpr не допускается, как показано ниже,

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

Однако, прочитав стандартный проект N3225 С++, я нигде не указал, что приведенный выше пример приведет к ошибке. В частности, из 7.1.5/9,

Спецификатор constexpr, используемый в объявление объекта объявляет объект как const. Такой объект должен иметь буквальный тип и должен быть инициализирован. Если он инициализируется конструктором вызов, конструктор должен быть конструктор constexpr и каждый аргумент конструктору должен быть постоянное выражение. этот звонок должен быть постоянным выражением (5.19). В противном случае каждое полное выражение, которое в его инициализаторе должно быть постоянное выражение.

Следовательно, если constexpr int limit = 2 * S:: size; недопустимо, тогда размер S:: size не должен быть константным выражением, а затем из 5.19 (постоянное выражение), я вижу, что стандартное отклонение 2 * S:: size в приведенном выше примере не является постоянным выражением.

Может ли кто-нибудь указать на все, что я упустил? Большое вам спасибо.

Ответы

Ответ 1

S:: size не является константным выражением в соответствии с N3225 §5.19p2:

Условное выражение является константным выражением, если оно не связано с одним из следующих...

  • преобразование lvalue-to-rvalue (4.1), если оно не применяется к
    • значение целочисленного или перечисляемого типа, которое относится к энергонезависимому объекту const с предшествующей инициализацией, инициализированным константным выражением, или
    • [другие условия, которые не применяются]

Обратите внимание на то, как вторая кодовая метка, которую я процитировал, позволяет интегральному статическому элементу данных, который сам инициализируется с постоянным выражением, также является постоянным выражением, но ваш размер S:: не инициализирован.

(Боковое примечание: константные выражения определяются в терминах условных выражений, потому что это то, как работает грамматика С++.)

Если вам интересно, как происходит преобразование lvalue-to-rvalue, см. §5p9:

Всякий раз, когда выражение glvalue появляется как операнд оператора, ожидающего prvalue для этого операнда, значение lvalue-to-rvalue (4.1), массив-указатель (4.2) или функция-to-pointer (4.3) стандартные преобразования применяются для преобразования выражения в prvalue.

Это, вероятно, хороший пример того, как чтение стандартного не делает хорошей ссылки, хотя еще не доступно еще для 0x.

Ответ 2

"каждое полное выражение, которое появляется в нем инициализатором, должно быть постоянным выражением"

S::size не является постоянным выражением, поэтому он не может появиться при инициализации константного выражения.