Подтвердить что ты не робот

Почему переменные статические члены, инициализированные в классе, должны быть 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, но почему это незаконно?

4b9b3361

Ответ 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 может быть только постоянным выражением, поэтому он является незаконным для всех нецелевых значений.