typedef char (&yes)[1];
typedef char (&no)[2];
template <typename B, typename D>
struct Host
{
operator B*() const;
operator D*();
};
template <typename B, typename D>
struct is_base_of
{
template <typename T>
static yes check(D*, T);
static no check(B*, int);
static const bool value = sizeof(check(Host<B,D>(), int())) == sizeof(yes);
};
//Test sample
class Base {};
class Derived : private Base {};
//Exspression is true.
int test[is_base_of<Base,Derived>::value && !is_base_of<Derived,Base>::value];
>注意B是私有基。这个怎么用?
>注意,运算符B *()是const。它为什么如此重要?
>为什么是template< typename T>静态是检查(D *,T);好于静态是检查(B *,int); ?
注意:这是boost :: is_base_of的缩减版本(宏被删除)。这适用于各种编译器。
让我们假设B实际上是D的基础。然后对于检查的调用,两个版本都是可行的,因为主机可以转换为D *和B *。它是来自主机< B,D>的由13.3.3.1.2描述的用户定义的转换序列。到D *和B *。为了找到可以转换类的转换函数,根据13.3.1.5/1为第一个检查函数合成以下候选函数
D* (Host<B, D>&)
第一个转换函数不是候选项,因为B *不能转换为D *。
对于第二个函数,存在以下候选项:
B* (Host<B, D> const&)
D* (Host<B, D>&)
这些是采用主机对象的两个转换函数候选。第一个用const引用,第二个不用。因此,第二个是通过13.3.3.2/3b1sb4更好地匹配非const * this对象(隐含对象参数),并且用于转换为第二个检查函数的B *。
如果你删除const,我们将有以下候选人
B* (Host<B, D>&)
D* (Host<B, D>&)
这意味着我们不能再选择constness了。在普通的重载解决方案中,该调用现在是不明确的,因为通常返回类型将不参与重载分辨率。然而,对于转换函数,有一个后门。如果两个转换函数同样好,那么它们的返回类型根据13.3.3 / 1决定谁是最好的。因此,如果你删除const,然后第一个将被采取,因为B *转换更好的B *比D *到B *。
现在什么用户定义的转换顺序更好?一个用于第二个还是第一个检查功能?规则是用户定义的转换序列只能根据13.3.3.2/3b2使用相同的转换函数或构造函数进行比较。这正是这种情况:两者都使用第二个转换函数。注意,因此const是重要的,因为它强制编译器采取第二个转换函数。
因为我们可以比较它们 – 哪一个更好?规则是,从转换函数的返回类型到目标类型的更好的转换获胜(再次是13.3.3.2/3b2)。在这种情况下,D *比B *更好地转换为D *。因此,第一个函数被选中,我们承认继承!
注意,由于我们从来不需要实际转换为基类,因此我们可以识别私有继承,因为我们是否可以从D *转换为B *不依赖于依据4.10 / 3的继承形式
如果他们不相关
现在让我们假设它们与继承无关。因此,对于第一个函数,我们有以下候选
D* (Host<B, D>&)
而对于第二个,我们现在有另一套
B* (Host<B, D> const&)
因为如果我们没有继承关系,我们不能将D *转换为B *,所以我们现在在两个用户定义的转换序列中没有共同的转换函数!因此,如果不是第一个函数是模板的事实,我们将是模糊的。当存在根据13.3.3 / 1同样好的非模板函数时,模板是第二选择。因此,我们选择非模板函数(第二个),我们认识到B和D之间没有继承!
让我们通过看看步骤来了解它的工作原理。
从sizeof开始(check(Host <B,D>(),int()))part。有两个候选重载可用,template <typename T> yes check(D *,T);和无校验(B *,int)。如果选择第一个,你得到sizeof(是),否则sizeof(no)接下来,让我们来看看重载分辨率。第一个重载是模板实例化检查<int>(D *,T = int),第二个候选是check(B *,int)。提供的实际参数是Host <B,D>和int()。第二个参数明显不区分它们;它只是使第一个重载模板一。我们稍后会看到为什么模板部分是相关的。
现在看看需要的转换序列。对于第一个重载,我们有Host <B,D> :: operator D * - 一个用户定义的转换。对于第二个,过载是棘手的。我们需要一个B *,但可能有两个转换序列。一个是通过Host <B,D> :: operator B *()const。如果(且仅当)B和D通过继承相关,将存在转换序列Host <B,D> :: operator D *()+ D * - > B *。现在假设D确实从B继承。两个转换序列是Host <B,D> - > Host <B,D> const-> operator B * const-> B *和Host <B,D> - > - > D * - > B *。(Host <B,D>- > D * - > B *)
模板函数选择最匹配的优先推导的,这里的
sizeof(check(Host<B,D>(), int()))第一个参数是int,所以优先推导匹配
static no check(B*, int);这个函数,但会引起歧义,只能选第一个
所以对于
static no check(B*, int); 来说,是有歧义的,所以只能选择模板static yes check(D*, T);所以,对于相关的B和D,没有检查(<Host <B,D>(),int())会模糊。结果,选择模板化的是检查<int>(D *,int)。然而,如果D不从B继承,则没有检查(<Host <B,D>(),int())不是模糊的。在这一点上,过载分辨率不能发生在最短的转换序列上。然而,给定相等的转换序列,重载分辨率优选非模板函数,即不检查(B *,int)。
你现在看到为什么继承是私有的:这种关系只是用来消除在访问检查发生之前没有检查(主机<B,D>(),int())从重载解决。你还可以看到为什么操作符B * const必须是const:否则就没有Host <B,D> - > Host <B,D> const step这一步,没有歧义,因此没有检查(static no check(B*, int))总是被选择。