Я просматриваю код 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 { }
Разве первый экземпляр не должен иметь явную истину в своих параметрах шаблона, а затем срабатывает специализация шаблона?
Сначала вы определяете шаблон. Этот «базовый» шаблон — это то, что вы получите, если попытаетесь создать экземпляр шаблона.
Затем вы можете определить специализацию. Если параметры шаблона совпадают со специализацией, то вы получаете специализированный шаблон. Сюрприз!
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
, а специализация — когда его нет.