C++ 模板特化如何使用默认布尔值?

问题描述 投票:0回答:2

我正在查看 libc++ 的代码,我注意到了这个片段:

// __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
的第一个实现,如果不是,我们就使用第二个(显式为假的那个)在其模板参数中)。我很困惑,因为我不明白这是如何工作的,第一个实例不应该在其模板参数中有一个明确的
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 { }
c++ template-meta-programming sfinae
2个回答
1
投票

第一个实例不应该在其模板参数中有一个明确的 true 然后模板专业化开始吗?

首先,您定义一个模板。如果您尝试实例化一个模板,就会得到这个“基本”模板。

然后你可以定义一个专业化。如果模板参数与专业化匹配,那么您将获得专业化模板。惊喜!

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

让我们忽略专业化。如果没有专门化,这是您在实例化此模板时将获得的模板。这就是当最后一个模板参数是

true
false
时得到的结果。没关系。这是你的模板。好好享受。祝你胃口好

但是等等,你忘了:你也有专长。别急:如果最后一个模板参数是

false
,你的餐点就是专业化。

但是,如果最后一个模板参数原来是

true
没有任何变化,你仍然得到原始模板。其他地方都不需要设置为“显式真实”。

话虽如此,是的,您实际上可以通过 two 专业化,甚至不定义基本模板,使事情看起来像这样:

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*;
};

这在逻辑上是等价的。这两种选择在逻辑上是等价的。


1
投票

这只是一个类型特征,用于使用类的部分专业化 分派到特定的实现。有

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
成员时使用主模板,而当它没有时使用专业化

© www.soinside.com 2019 - 2024. All rights reserved.