Читать «Эффективный и современный С++. 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
, должна давать результат 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);