这种情况会发生什么:
struct A {
void f();
};
struct B : virtual A {
using A::f;
};
struct C : virtual A {
using A::f;
};
struct D : B, C {
void g() {
f();
}
};
感兴趣的行是f()
。显然,根据FDIS的f
查找10.2
成功并找到A::f
。但是,哪些候选人会考虑重载决议?该规范在13.3.1p4
:
对于由using声明引入到派生类的非转换函数,该函数被认为是派生类的成员,用于定义隐式对象参数的类型。
这样做的目的是,对于单个类,如果这样的类包含自己的成员函数和使用基类函数名称的using声明,那么在重载解析期间,所有候选函数都具有相同的类类型。他们的隐含对象参数。但这对于上面的例子意味着什么呢?候选人会是以下人员吗?
void F1(B&)
void F2(C&)
// call arguments: (lvalue D)
这似乎是错误的,因为根据10.2p7
我们在查找结果集中只有一个声明。我们该怎么解释这个?
答案 0 :(得分:1)
我认为,由于10.2 / 7产生的查找集只产生一个声明,所以根本没有函数重载。 13.3.1 / 4仅适用于10.2 / 7产生的查找集包含两个或多个声明的情况。
编辑:也许我并不像我希望的那样清晰。即使f
中的A
超载,我认为大多数相同的推理都适用。也许最好一步一步地采取行动。 (注意,在这种情况下,我使用相同的S(f,X)表示法作为标准,但由于你的派生类最多是D,你的S(f,D)对应于它们的S(f,C) ,你的S(f,B)和S(f,C)对应于它的S(f,B 1 )和S(f,B 2 )。 / p>
首先s(f,D)是空的,因为我们没有直接包含在D中的f声明。基于此,我们得到10.2 / 5.
在10.2 / 6中,我们首先将s(f,B)合并为S(f,D)。由于s(f,D)当前为空,我们遵循第一个项目符号点下的第二个条件,S(f,D)成为S(f,B)的副本。
然后我们必须将S(f,C)合并为S(f,D)。在这种情况下,S(f,C)的每个子对象成员是S(f,D)的子对象成员。这满足第一个项目符号点的第一个条件,因此我们保持S(f,D)不变,并且合并完成。
此时,不再考虑基类B i ,因此我们的S(f,D)= S(f,B)。来自S(f,C)的声明的无出现在所有的最终重载集中。
然后,如果S(f,B)包含两个或更多个函数,我们进入13.3.1,并解决过载集 - 但由于整个集合来自B,问题中提出的情况根本就没有存在。
答案 1 :(得分:0)
只有猜测,完全不确定。 :)子>
[ Example:
struct A { int x; }; // S(x,A) = { { A::x }, { A } }
struct B { float x; }; // S(x,B) = { { B::x }, { B } }
struct C: public A, public B { }; // S(x,C) = { invalid, { A in C, B in C } }
struct D: public virtual C { }; // S(x,D) = S(x,C)
struct E: public virtual C { char x; }; // S(x,E) = { { E::x }, { E } }
struct F: public D, public E { }; // S(x,F) = S(x,E)
int main() {
F f;
f.x = 0; // OK, lookup finds E::x
}
S(x, F) is unambiguous because the A and B base subobjects of D are also base subobjects of E, so S(x,D)
is discarded in the first merge step. —end example ]
是10.2p7的示例,其中S(f,C)
表示查找集。最后提供的句子是必不可少的:由于D
和E
都有相同的C
基类,E::x
隐藏了 {{ 1}}来自那个x
,最后使用C
无关紧要
现在,在您的示例中,没有任何内容隐藏F::x
的基类的f
,因此使用D
仍然是不明确的,我无法看到10.2p7如何适用于您的案例。就像在顶部说的那样,完全不确定。 ;)
答案 2 :(得分:0)
我没有直接解决这个问题,而是试图争辩说假装每个派生类中存在f
函数都不起作用:
只有一个候选函数,它有类型
void A::(void)
虽然您可以使用
形成指向该功能的指针void (A::*F0)(void) = &A::f;
void (B::*F1)(void) = F0;
void (C::*F2)(void) = F0;
这是因为指向成员的指针包含计算函数参数所需的附加信息。指向成员的指针调用站点找到实际目标实例的A
子对象,以提供this
指针f
。函数内部没有逻辑可以从派生类型的this
指针中找到A的成员。因此,正如您的问题所示,我们无法谈论void F1(B* this)
和void F2(C* this)
。
如果函数被认为是派生类的成员,则为
void B::A::f(void);
void C::A::f(void);
但由于B::A
和C::A
是相同的基类,最终候选列表中只有一个函数,尽管它在列表中两次。然后虚拟继承提供了两个候选者在同一个对象上调用相同的函数,没有歧义。
答案 3 :(得分:0)
我认为关键是在10.2p5中,标准是指检查每个“直接基类子对象”。
因为A
是虚拟继承的,所以它是D
(10.1p4)的“直接基类子对象”。
然后考虑D
的三个子对象:A
,B
和C
。通过10.2p6 B
和C
被删除(A
是这些的基础),只有A::f
是候选者。