`is_base_of'如何工作? [英] How does `is_base_of` work?
问题描述
以下代码如何工作?
typedef char(& yes)[1]
typedef char(& no)[2];
template< typename B,typename D>
struct Host
{
运算符B *()const;
operator D *();
};
template< typename B,typename D>
struct is_base_of
{
template< typename T>
static yes check(D *,T);
静态无检查(B *,int);
static const bool value = sizeof(check(Host< B,D>(),int()))== sizeof
};
//测试样例
class Base {};
class Derived:private Base {};
//暴露是真的。
int test [is_base_of< Base,Derived> :: value&& !is_base_of< Derived,Base> :: value];
注意
code>是私人基地。这个怎么用?
请注意,
运算符B *()
是const。为什么很重要?
为什么是
template< typename T&静态是检查(D *,T);
好于static yes check(B *,int);
?
注意:<$ c $的缩减版c> boost :: is_base_of 。
解决方案
如果它们是相关的
让我们暂且假设
B
实际上是D
的基础。然后对于check
的调用,两个版本都是可行的,因为Host
可以转换为D *
和B *
。它是由13.3.3.1.2
从主机< B,D>
到D *
和B *
。为了找到可以转换该类的转换函数,根据13.3.1.5/1 $来为第一个
检查
函数合成以下候选函数c $ c>D *(主机< B,D&&;)
第一个转换函数不是候选项,因为
B *
到D *
。
对于第二个函数,存在以下候选项:
B *(Host< B,D> const&)
D *(Host< B,D &&)
b $ b这些是接受主机对象的两个转换函数候选。第一个用const引用,第二个不用。因此,第二个是由
13.3更好地匹配非const
,用于为第二个* this
对象( implied object argument )。 3.2 / 3b1sb4检查
函数转换B *
如果您删除常量,我们将有以下候选人
:
B *(Host< B,D&)
pre>
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 *
更好地转换为D *
比B *
。因此,选择第一个函数,并识别继承!
请注意,由于我们从来不需要实际转换为基类,因此我们可以从而识别私人继承,因为我们是否可以从
D *
转换为B *
根据4.10 / 3
如果它们不相关,则不依赖于继承的形式
现在让我们假设它们没有继承关系。因此,对于第一个函数,我们有以下候选
D *(Host< B,D&
而对于第二个,我们现在有另一个
B *(Host< B,D> const&)
如果我们没有继承关系,我们不能将
D *
转换为B *
函数在两个用户定义的转换序列之间!因此,如果不是因为第一个函数是模板,我们会模糊。当有一个非模板函数根据13.3.3 / 1
同样好的时候,模板是第二选择。因此,我们选择非模板函数(第二个),并且我们认识到B
和D
!How does the following code work?
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];
Note that
B
is private base. How does this work?Note that
operator B*()
is const. Why is it important?Why is
template<typename T> static yes check(D*, T);
better thanstatic yes check(B*, int);
?Note: It is reduced version (macros are removed) of
boost::is_base_of
. And this works on wide range of compilers.解决方案If they are related
Let's for a moment assume that
B
is actually a base ofD
. Then for the call tocheck
, both versions are viable becauseHost
can be converted toD*
andB*
. It's a user defined conversion sequence as described by13.3.3.1.2
fromHost<B, D>
toD*
andB*
respectively. For finding conversion functions that can convert the class, the following candidate functions are synthesized for the firstcheck
function according to13.3.1.5/1
D* (Host<B, D>&)
The first conversion function isn't a candidate, because
B*
can't be converted toD*
.For the second function, the following candidates exist:
B* (Host<B, D> const&) D* (Host<B, D>&)
Those are the two conversion function candidates that take the host object. The first takes it by const reference, and the second doesn't. Thus the second is a better match for the non-const
*this
object (the implied object argument) by13.3.3.2/3b1sb4
and is used to convert toB*
for the secondcheck
function.If you would remove the const, we would have the following candidates
B* (Host<B, D>&) D* (Host<B, D>&)
This would mean that we can't select by constness anymore. In an ordinary overload resolution scenario, the call would now be ambiguous because normally the return type won't participate in overload resolution. For conversion functions, however, there is a backdoor. If two conversion functions are equally good, then the return type of them decides who is best according to
13.3.3/1
. Thus, if you would remove the const, then the first would be taken, becauseB*
converts better toB*
thanD*
toB*
.Now what user defined conversion sequence is better? The one for the second or the first check function? The rule is that user defined conversion sequences can only be compared if they use the same conversion function or constructor according to
13.3.3.2/3b2
. This is exactly the case here: Both use the second conversion function. Notice that thus the const is important because it forces the compiler to take the second conversion function.Since we can compare them - which one is better? The rule is that the better conversion from the return type of the conversion function to the destination type wins (again by
13.3.3.2/3b2
). In this case,D*
converts better toD*
than toB*
. Thus the first function is selected and we recognize the inheritance!Notice that since we never needed to actually convert to a base class, we can thereby recognize private inheritance because whether we can convert from a
D*
to aB*
isn't dependent on the form of inheritance according to4.10/3
If they are not related
Now let's assume they are not related by inheritance. Thus for the first function we have the following candidates
D* (Host<B, D>&)
And for the second we now have another set
B* (Host<B, D> const&)
Since we cannot convert
D*
toB*
if we haven't got a inheritance relationship, we now have no common conversion function among the two user defined conversion sequences! Thus, we would be ambiguous if not for the fact that the first function is a template. Templates are second choice when there is a non-template function that is equally good according to13.3.3/1
. Thus, we select the non-template function (second one) and we recognize that there is no inheritance betweenB
andD
!这篇关于`is_base_of'如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!