Почему переменные статические члены, инициализированные в классе, должны быть constexpr?
Элементы статического интегрального элемента, инициализированные в определении класса, могут быть объявлены const
или constexpr
, но нецелые элементы статических данных, инициализированные в определении класса, должны быть constexpr
:
class MyClass {
static const int w = 5; // okay
static constexpr int x = 5; // okay
static const float y = 1.5; // error!
static constexpr float z = 1.5; // okay
};
Кто-нибудь знает, почему объявление для y не разрешено? Часть стандарта делает его незаконным 9.4.2/3, но почему это незаконно?
Ответы
Ответ 1
До С++ 11 вы не могли инициализировать статические члены неинтегральных/перечисляемых типов в объявлении класса (но вы можете за пределами объявления класса). Правило, управляющее constexpr
, переносит это вперед, но позволяет инициализировать его с помощью constexpr
в объявлении класса (так что вам больше не нужен код, как показано ниже):
struct A
{
static const float pi;
};
const float A::pi = 3.1415;
Одним из побочных эффектов этого правила было упрощение вашей структуры класса, а не уродство (например, приведенный выше код).
Одной из причин, почему это было до добавления С++ 11 constexpr
, был стандарт, который не указывал, как должны были быть реализованы плавающие точки (он остается для процессора/архитектуры - например, когда вы говорите float x = 1.6f
, это фактически 1.6000000000024
для большинства систем).
Ответ 2
float
немного сложнее описать мотивацию, но представьте себе члена класса:
class MySpecialInt {
public:
constexpr MySpecialInt(const int & other) {
}
};
class MyClass {
static const MySpecialInt a = 5; // error
static constexpr MySpecialInt b = 5; // okay
};
a
в этом сценарии может иметь некоторую нетривиальную конструкцию, которая потенциально может нарушать (или, по крайней мере, грубо усложнять) правило с одним определением. Поскольку constexpr
имеет гарантированные ограничительные свойства времени компиляции, конструктор копирования b
должен также быть constexpr
и поэтому гарантированно возвращает корректно определенное значение во время компиляции (и NOT нарушает одно -определение-правило)
Почему float
проявляет такое поведение, я считаю, что это только по причинам, связанным с наследием, поскольку float
никогда не традиционно инициализировался таким образом ( "потому что стандарт говорит так" ), поэтому они поймали инициализацию static
const
float
членов под зонтиком constexpr
.
Ответ 3
Возможно, из-за того, что неединичный я может также включать в себя тип данных типа char, и поэтому вы не можете сделать их постоянными и нуждаться в постоянном выражении. Но в случае интеграла вы можете либо сделать их постоянными выражения или константы. Таким образом, из-за того, что char может быть только постоянным выражением, поэтому он является незаконным для всех нецелевых значений.