Как специализация шаблона C++ работает с логическим значением по умолчанию?

Я просматриваю код libС++ и заметил этот фрагмент:

// __pointer
_LIBCPP_ALLOCATOR_TRAITS_HAS_XXX(__has_pointer, pointer);
template <class _Tp, class _Alloc,
          class _RawAlloc = __libcpp_remove_reference_t<_Alloc>,
          bool = __has_pointer<_RawAlloc>::value>
struct __pointer {
    using type _LIBCPP_NODEBUG = typename _RawAlloc::pointer;
};
template <class _Tp, class _Alloc, class _RawAlloc>
struct __pointer<_Tp, _Alloc, _RawAlloc, false> {
    using type _LIBCPP_NODEBUG = _Tp*;
};

Что меня озадачило, так это строчка:

bool = __has_pointer<_RawAlloc>::value

Семантика этого кода довольно ясна: здесь мы вызываем метафункцию __has_pointer, и если она верна, мы идем с первой реализацией __pointer, а если нет, мы идем со второй (той, у которой явное значение false в параметре шаблона) . Я в замешательстве, потому что не понимаю, как это работает, разве первый экземпляр не должен иметь явное true в параметрах шаблона, а затем срабатывает специализация шаблона? Если это так, то нам нужно вызвать метафункцию, когда мы инициируем этот шаблон, поэтому, может быть, bool = __has_pointer<_RawAlloc>::value> является сокращением для этого? Мне интересно, какой механизм здесь используется, чтобы это было разрешено. Какие правила используются для этой работы?

Полную реализацию _LIBCPP_ALLOCATOR_TRAITS_HAS_XXX можно найти здесь:

#define _LIBCPP_ALLOCATOR_TRAITS_HAS_XXX(NAME, PROPERTY)            \
template <class _Tp, class = void> struct NAME : false_type { };    \
template <class _Tp>               struct NAME<_Tp, __void_t<typename _Tp:: PROPERTY > > : true_type { }
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
86
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Разве первый экземпляр не должен иметь явную истину в своих параметрах шаблона, а затем срабатывает специализация шаблона?

Сначала вы определяете шаблон. Этот «базовый» шаблон — это то, что вы получите, если попытаетесь создать экземпляр шаблона.

Затем вы можете определить специализацию. Если параметры шаблона совпадают со специализацией, то вы получаете специализированный шаблон. Сюрприз!

template <class _Tp, class _Alloc,
          class _RawAlloc = __libcpp_remove_reference_t<_Alloc>,
          bool = __has_pointer<_RawAlloc>::value>
struct __pointer

Оставим без внимания специализацию. Без специализации это шаблон, который вы получите при создании экземпляра этого шаблона. Это то, что вы получаете, когда последний параметр шаблона оказывается либо true, либо false. Это не имеет значения. Это ваш шаблон. Наслаждайся этим. Приятного аппетита.

Но подождите, вы забыли: у вас тоже есть специализация. Придержи коней: если последним параметром шаблона окажется false, специализацией станет твоя еда.

Однако, если последний параметр шаблона окажется true ничего не изменится, вы все равно получите исходный шаблон. Ничто не должно быть установлено как «явное истинное» в другом месте.

Сказав это, да, вы действительно можете заставить вещи выглядеть так, имея две специализации и даже не определяя базовый шаблон:

template <class _Tp, class _Alloc,
          class _RawAlloc = __libcpp_remove_reference_t<_Alloc>,
          bool = __has_pointer<_RawAlloc>::value>
struct __pointer;

template <class _Tp, class _Alloc, class _RawAlloc>
struct __pointer<_Tp, _Alloc, _RawAlloc, true> {
    using type _LIBCPP_NODEBUG = typename _RawAlloc::pointer;
};

template <class _Tp, class _Alloc, class _RawAlloc>
struct __pointer<_Tp, _Alloc, _RawAlloc, false> {
    using type _LIBCPP_NODEBUG = _Tp*;
};

Это логически эквивалентно. Обе альтернативы логически эквивалентны друг другу.

Это просто черта типа, используемая для отправки в конкретную реализацию с использованием частичной специализации класса. С

template <class _Tp, class _Alloc,
          class _RawAlloc = __libcpp_remove_reference_t<_Alloc>,
          bool = __has_pointer<_RawAlloc>::value>
struct __pointer {
    using type _LIBCPP_NODEBUG = typename _RawAlloc::pointer;
};

Определяется шаблон основного класса, и часть bool = __has_pointer<_RawAlloc>::value шаблона представляет собой bool нетиповой параметр tmplate, который по умолчанию имеет значение __has_pointer<_RawAlloc>::value. __has_pointer<_RawAlloc>::value вернет true, если у _RawAlloc есть pointer участник и false в противном случае.

Далее у нас есть

template <class _Tp, class _Alloc, class _RawAlloc>
struct __pointer<_Tp, _Alloc, _RawAlloc, false> {
    using type _LIBCPP_NODEBUG = _Tp*;
};

который является частичной специализацией основного шаблона и будет использоваться всякий раз, когда последним параметром шаблона является false.

Это означает, что основной шаблон используется, когда у _RawAlloc есть член pointer, а специализация — когда его нет.

Другие вопросы по теме