Смешивать использование 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 могут использоваться взаимозаменяемо? Конечно, функции contexpr не могут применяться к const, поэтому я имею в виду данные const и данные constexpr.
Я прочитал какой-то стандартный черновик и предложение в http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf, но меня это смущает. Итак, у меня есть еще несколько вопросов,
В N2235 четко указано, что данные const не обязательно являются константами времени компиляции, см. Следующий пример:
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;
Однако, прочитав черновик стандарта C++ N3225, я нигде не вижу явно заявленного, что приведенный выше пример вызовет ошибку. В частности, из 7.1.5/9,
Спецификатор constexpr, используемый в объявлении объекта, объявляет объект как const. Такой объект должен иметь буквальный тип и должен быть инициализирован. Если он инициализируется вызовом конструктора, конструктор должен быть конструктором constexpr, и каждый аргумент конструктора должен быть константным выражением. этот вызов должен быть постоянным выражением (5.19). В противном случае каждое полное выражение, которое появляется в его инициализаторе, должно быть константным выражением.
Следовательно, если constexpr int limit = 2 * S::size; неверно, то S:: size не должно быть константным выражением, тогда из 5.19 (константное выражение) я нигде не вижу стандартного запрета 2 * S:: size в приведенном выше примере, чтобы не быть константным выражением.
Кто-нибудь может указать на что-то, что я упустил? Большое спасибо.
2 ответа
S::size не является константным выражением в соответствии с N3225 §5.19p2:
Условное выражение является константным выражением, если оно не включает одно из следующих…
- преобразование lvalue в rvalue (4.1), если оно не применяется к
- glvalue целочисленного типа или типа перечисления, которое относится к энергонезависимому константному объекту с предшествующей инициализацией, инициализированной с помощью константного выражения, или
- [другие условия, которые не применяются]
Обратите внимание на то, что вторая приведенная мною точка маркера позволяет встроенному статическому члену данных, который сам по себе инициализируется с помощью константного выражения, также быть константным выражением, но ваш S::size неинициализирован.
(Примечание: константные выражения определены в терминах условных выражений, потому что именно так работает грамматика C++.)
Если вам интересно, как происходит преобразование lvalue в rvalue, см. §5p9:
Всякий раз, когда выражение glvalue появляется в качестве операнда оператора, ожидающего значение prvalue для этого операнда, стандартными преобразованиями lvalue-to-rvalue (4.1), array-to-pointer (4.2) или function-to-pointer (4.3) являются: применяется для преобразования выражения в значение.
Это, вероятно, хороший пример того, как чтение стандарта не дает хороших ссылок, хотя для 0x пока еще мало что доступно.
"каждое полное выражение, которое появляется в его инициализаторе, должно быть постоянным выражением"
S::size
не является константным выражением, поэтому оно не может появляться при инициализации константного выражения.