Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 55

Скотт Мейерс

           std::string,   // Адрес

           std::size_t> ; // Репутация

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

UserInfo uInfo;                // Объект с типом кортежа

auto val = std::get<1>(uInfo); // Получение значения поля 1

Как программисту вам приходится отслеживать множество вещей. Вы действительно должны помнить, что поле 1 соответствует адресу электронной почты пользователя? Я думаю, нет. Использование enum без области видимости для сопоставления имен полей с их номерами позволяет избежать необходимости перегружать память:

enum UserInfoFields { uiName, uiEmail, uiReputation };

UserInfo uInfo;                      // Как и ранее

auto val = std::get<uiEmail>(uInfo); // Значение адреса

Все было бы гораздо сложнее без неявного преобразования значений из UserInfoFields в тип std::size_t, который является типом, требующимся для std::get.

Соответствующий код с применением перечисления с областью видимости существенно многословнее:

enum class UserInfoFields { uiName, uiEmail, uiReputation };

UserInfo uInfo; // Как и ранее

auto val =

 std::get<static_cast<std::size_t>(UserInfoFields::uiMail)>

  (uInfo);

Эта многословность может быть сокращена с помощью функции, которая принимает перечислитель и возвращает соответствующее значение типа std::size_t, но это немного сложнее. std::get является шаблоном, так что предоставляемое значение является аргументом шаблона (обратите внимание на применение не круглых, а угловых скобок), так что функция, преобразующая перечислитель в значение std::size_t, должна давать результат во время компиляции. Как поясняется в разделе 3.9, это означает, что нам нужна функция, являющаяся constexpr.

Фактически это должен быть constexpr-шаблон функции, поскольку он должен работать с любыми перечислениями. И если мы собираемся делать такое обобщение, то должны обобщить также и возвращаемый тип. Вместо того чтобы возвращать std::size_t, мы должны возвращать базовый тип перечисления. Он доступен с помощью свойства типа std::underlying_type (о свойствах типов рассказывается в разделе 3.3). Наконец мы объявим его как noexcept (см. раздел 3.8), поскольку мы знаем, что он никогда не генерирует исключений. В результате мы получим шаблон функции toUType, который получает произвольный перечислитель и может возвращать значение как константу времени компиляции:

template<typename E>

constexpr typename std::underlying_type<E>::type

toUType(E enumerator) noexcept

{

 return

  static_cast<typename

   std::underlying_type<E>::type>(enumerator);

}

В С++14 toUType можно упростить заменой typename std::underlying_type<E>::type  более изящным std::underlying_type_t (см. раздел 3.3):

template<typename E> // С++14

constexpr std::underlying_type_t<E>

toUType(E enumerator) noexcept {

 return static_cast<std::underlying_type_t<E>>(enumerator);