7.5.6. Литеральные классы

В разделе 6.5.2 упоминалось, что параметры и возвращаемое значение функции constexpr должны иметь литеральные типы. Кроме арифметических типов, ссылок и указателей, некоторые классы также являются литеральными типами. В отличие от других классов, у классов, являющихся литеральными типами, могут быть функции-члены constexpr. Такие функции-члены должны отвечать всем требованиям функций constexpr. Эти функции-члены неявно константные (см. раздел 7.1.2).

Агрегатный класс (см. раздел 7.5.5), все переменные-члены которого имеют литеральный тип, является литеральным классом. Неагрегатный класс, соответствующий следующим ограничениям, также является литеральным классом.

• У всех переменных-членов должен быть литеральный тип.

• У класса должен быть по крайней мере один конструктор constexpr.

• Если у переменной-члена есть внутриклассовый инициализатор, инициализатор для переменной-члена встроенного типа должен быть константным выражением (см. раздел 2.4.4). Если переменная-член имеет тип класса, инициализатор должен использовать его собственный конструктор constexpr.

• Класс должен использовать заданное по умолчанию определение для своего деструктора — функции-члена класса, удаляющего объекты типа класса (см. раздел 7.1.5).

Конструкторы constexpr

Хотя конструкторы не могут быть константными (см. раздел 7.1.4), в литеральном классе они могут быть функциями constexpr (см. раздел 6.5.2). Действительно, литеральный класс должен предоставлять по крайней мере один конструктор constexpr.

Конструктор constexpr может быть объявлен как = default (см. раздел 7.1.4) или как удаленная функция, которые будут описаны в разделе 13.1.6. В противном случае конструктор constexpr должен отвечать требованиям к конструкторам (у него не может быть оператора return) и к функциям constexpr (его исполняемый оператор может иметь единственный оператор return) (см. раздел 6.5.2). В результате тело конструктора constexpr обычно пусто. Определению конструктора constexpr предшествует ключевое слово constexpr:

class Debug {

public:

 constexpr Debug(bool b = true): hw(b), io(b), other(b) { }

 constexpr Debug(bool h, bool i, bool o):

                 hw(h), io(i), other(o) { }

 constexpr bool any() { return hw || io || other; }

 void set_io(bool b) { io = b; }

 void set_hw(bool b) { hw = b; }

 void set_other(bool b) { hw = b; }

private:

 bool hw;    // аппаратная ошибка, отличная от ошибки IO

 bool io;    // ошибка IO

 bool other; // другие ошибки

};

Конструктор constexpr должен инициализировать каждую переменную-член. Инициализаторы должны либо использовать конструктор constexpr, либо быть константным выражением.

Конструктор constexpr используется и для создания объектов constexpr, и для параметров или типов возвращаемого значения функций constexpr:

constexpr Debug io_sub(false, true, false); // отладка IO

if (io_sub.any())                           // эквивалент if (true)

 cerr << "print appropriate error messages" << endl;

constexpr Debug prod(false);                // при выпуске без отладки

if (prod.any())                             // эквивалент if (false)

 cerr << "print an error message" << endl;

Упражнения раздела 7.5.6

Упражнение 7.53. Определите собственную версию класса Debug.

Упражнение 7.54. Должны ли члены класса Debug, начинающиеся с set_, быть объявлены как constexpr? Если нет, то почему?

Упражнение 7.55. Является ли класс Data из раздела 7.5.5 литеральным? Если нет, то почему? Если да, то почему он является литеральным.

Более 800 000 книг и аудиокниг! 📚

Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением

ПОЛУЧИТЬ ПОДАРОК