C++ `is_base_of` 是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2910979/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does `is_base_of` work?
提问by Alexey Malistov
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 {};
//Expression 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);
?
请注意,这
B
是私人基地。这是如何运作的?请注意,这
operator B*()
是常量。它为什么如此重要?为什么
template<typename T> static yes check(D*, T);
比 好static 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.
注意:它是boost::is_base_of
. 这适用于广泛的编译器。
采纳答案by Johannes Schaub - litb
If they are related
如果他们有关系
Let's for a moment assume that B
is actually a base of D
. Then for the call to check
, both versions are viable because Host
can be converted to D*
andB*
. It's a user defined conversion sequence as described by 13.3.3.1.2
from Host<B, D>
to D*
and B*
respectively. For finding conversion functions that can convert the class, the following candidate functions are synthesized for the first check
function according to 13.3.1.5/1
让我们暂时假设它B
实际上是D
. 然后对于调用check
,两个版本都是可行的,因为Host
可以转换为D*
和B*
。它是用户定义的转换序列,分别由13.3.3.1.2
from Host<B, D>
toD*
和from描述B*
。为了找到可以转换类的转换函数,check
根据第一个函数合成以下候选函数13.3.1.5/1
D* (Host<B, D>&)
The first conversion function isn't a candidate, because B*
can't be converted to D*
.
第一个转换函数不是候选函数,因为B*
不能转换为D*
.
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) by 13.3.3.2/3b1sb4
and is used to convert to B*
for the second check
function.
这些是采用宿主对象的两个转换函数候选者。第一个通过 const 引用获取它,第二个没有。因此,第二个是非常量*this
对象(隐含的对象参数)的更好匹配by13.3.3.2/3b1sb4
并且用于转换B*
为第二个check
函数。
If you would removethe 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, because B*
converts better to B*
than D*
to B*
.
这意味着我们不能再通过常量进行选择。在普通的重载解析方案中,调用现在是不明确的,因为通常返回类型不会参与重载解析。然而,对于转换函数,有一个后门。如果两个转换函数一样好,那么他们的返回类型决定谁是最好的13.3.3/1
。因此,如果您要删除 const,则将采用第一个,因为B*
转换为 toB*
比D*
to更好B*
。
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 constis important because it forces the compiler to take the second conversion function.
现在什么用户定义的转换顺序更好?用于第二个或第一个检查功能的那个?规则是用户定义的转换序列只能在它们根据 使用相同的转换函数或构造函数时进行比较13.3.3.2/3b2
。这正是这里的情况:两者都使用第二个转换函数。请注意,因此const很重要,因为它强制编译器采用第二个转换函数。
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 to D*
than to B*
. Thus the first function is selected and we recognize the inheritance!
既然我们可以比较它们——哪个更好?规则是从转换函数的返回类型到目标类型的更好的转换获胜(再次通过13.3.3.2/3b2
)。在这种情况下,D*
转换为D*
比转换为 更好B*
。因此选择了第一个功能,我们识别了继承!
Notice that since we never needed to actuallyconvert to a base class, we can thereby recognize private inheritancebecause whether we can convert from a D*
to a B*
isn't dependent on the form of inheritance according to 4.10/3
请注意,由于我们从来不需要实际转换为基类,因此我们可以识别私有继承,因为我们是否可以从 a 转换D*
为 aB*
不依赖于根据继承的形式4.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*
to B*
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 ambiguousif 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 to 13.3.3/1
. Thus, we select the non-template function (second one) and we recognize that there is no inheritance between B
and D
!
由于我们没有继承关系就不能转换D*
为B*
,所以我们现在在两个用户定义的转换序列之间没有通用的转换函数!因此,如果不是因为第一个函数是模板,我们将是模棱两可的。当根据 存在同样好的非模板函数时,模板是第二选择13.3.3/1
。因此,我们选择了非模板函数(第二个)并且我们认识到B
和之间没有继承D
!
回答by MSalters
Let's work out how it works by looking at the steps.
让我们通过查看步骤来了解它是如何工作的。
Start with the sizeof(check(Host<B,D>(), int()))
part. The compiler can quickly see that this check(...)
is a function call expression, so it needs to do overload resolution on check
. There are two candidate overloads available, template <typename T> yes check(D*, T);
and no check(B*, int);
. If the first is chosen, you get sizeof(yes)
, else sizeof(no)
从sizeof(check(Host<B,D>(), int()))
零件开始。编译器可以很快看出这check(...)
是一个函数调用表达式,因此需要对 进行重载解析check
。有两个候选重载可用,template <typename T> yes check(D*, T);
和no check(B*, int);
. 如果选择第一个,你会得到sizeof(yes)
,否则sizeof(no)
Next, let's look at the overload resolution. The first overload is a template instantiation check<int> (D*, T=int)
and the second candidate is check(B*, int)
. The actual arguments provided are Host<B,D>
and int()
. The second parameter clearly doesn't distinguish them; it merely served to make the first overload a template one. We'll see later why the template part is relevant.
接下来,让我们看看重载决议。第一个重载是模板实例化check<int> (D*, T=int)
,第二个候选是check(B*, int)
. 提供的实际参数是Host<B,D>
和int()
。第二个参数显然没有区分它们;它只是使第一个重载成为模板。我们稍后会看到为什么模板部分是相关的。
Now look at the conversion sequences that are needed. For the first overload, we have Host<B,D>::operator D*
- one user-defined conversion. For the second, the overload is trickier. We need a B*, but there are possibly two conversion sequences. One is via Host<B,D>::operator B*() const
. If (and only if) B and D are related by inheritance will the conversion sequence Host<B,D>::operator D*()
+ D*->B*
exist. Now assume D indeed inherits from B. The two conversion sequences are Host<B,D> -> Host<B,D> const -> operator B* const -> B*
and Host<B,D> -> operator D* -> D* -> B*
.
现在查看所需的转换序列。对于第一个重载,我们有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> -> operator D* -> D* -> B*
。
So, for related B and D, no check(<Host<B,D>(), int())
would ambiguous. As a result, the templated yes check<int>(D*, int)
is chosen. However, if D does not inherit from B, then no check(<Host<B,D>(), int())
is not ambiguous. At this point, overload resolution cannot happen based on shortest conversion sequence. However, given equal conversion sequences, overload resolution prefers non-template functions, i.e. no check(B*, int)
.
所以,对于相关的B和D,no check(<Host<B,D>(), int())
就会产生歧义。结果,yes check<int>(D*, int)
选择了模板化。但是,如果 D 不是从 B 继承的,那么no check(<Host<B,D>(), int())
就没有歧义。此时,无法基于最短转换序列进行重载解析。然而,给定相等的转换序列,重载决议更喜欢非模板函数,即no check(B*, int)
.
You now see why it doesn't matter that the inheritance is private: that relation only serves to eliminate no check(Host<B,D>(), int())
from overload resolution before the access check happens. And you also see why the operator B* const
must be const: else there's no need for the Host<B,D> -> Host<B,D> const
step, no ambiguity, and no check(B*, int)
would always be chosen.
您现在明白为什么继承是私有的并不重要:该关系仅用于no check(Host<B,D>(), int())
在访问检查发生之前从重载解析中消除。你也明白为什么operator B* const
必须是常量:否则就不需要Host<B,D> -> Host<B,D> const
步骤,没有歧义,并且no check(B*, int)
总是会被选中。
回答by Matthieu M.
The private
bit is completely ignored by is_base_of
because overload resolution occurs before accessibility checks.
由于重载解析发生在可访问性检查之前private
,is_base_of
因此该位被完全忽略。
You can verify this simply:
您可以简单地验证这一点:
class Foo
{
public:
void bar(int);
private:
void bar(double);
};
int main(int argc, char* argv[])
{
Foo foo;
double d = 0.3;
foo.bar(d); // Compiler error, cannot access private member function
}
The same applies here, the fact that B
is a private base does not prevent the check from taking place, it would only prevent the conversion, but we never ask for the actual conversion ;)
这同样适用于这里,B
作为私人基地的事实不会阻止检查的发生,它只会阻止转换,但我们从不要求实际转换;)
回答by sellibitze
It possibly has something to do with partial ordering w.r.t. overload resolution. D* is more specialized than B* in case D derives from B.
它可能与偏序重载解析有关。在 D 派生自 B 的情况下,D* 比 B* 更专业。
The exact details are rather complicated. You have to figure out the precedences of various overload resolution rules. Partial ordering is one. Lengths/kinds of conversion sequences is another one. Finally, if two viable functions are deemed equally good, non-templates are chosen over function templates.
具体细节相当复杂。您必须弄清楚各种重载解析规则的优先级。部分排序是其中之一。转换序列的长度/种类是另一回事。最后,如果两个可行的函数被认为同样好,则选择非模板而不是函数模板。
I've never needed to look up how these rules interact. But it seems partial ordering is dominating the other overload resolution rules. When D doesn't derive from B the partial ordering rules don't apply and the non-template is more attractive. When D derives from B, partial ordering kicks in and makes the function template more attractive -- as it seems.
我从来不需要查看这些规则是如何相互作用的。但似乎偏序支配了其他重载解析规则。当 D 不是从 B 派生出来时,偏序规则不适用,非模板更有吸引力。当 D 派生自 B 时,偏序开始起作用并使函数模板更具吸引力——正如它看起来的那样。
As for inheritance being privete: the code never asks for a conversion from D* to B* which would require public inheritence.
至于继承是私有的:代码从不要求从 D* 到 B* 的转换,这需要公共继承。
回答by Hertz
Following on your second question, note that if it weren't for const, Host would be ill-formed if instantiated with B == D. But is_base_of is designed such that each class is a base of itself, hence one of conversion operators must be const.
在第二个问题之后,请注意,如果不是 const,如果使用 B == D 实例化 Host 将是格式错误的。但是 is_base_of 的设计使得每个类都是其自身的基础,因此转换运算符之一必须是常量。