15

这种情况应该发生什么:

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()。显然f根据10.2FDIS 的查找成功并找到A::f。但是,超载决议会考虑哪些候选人?规范说13.3.1p4

对于通过 using 声明引入派生类的非转换函数,该函数被认为是派生类的成员,以便定义隐式对象参数的类型。

这样做的目的是,对于单个类,如果此类包含自己的成员函数和将基类函数的名称带入作用域的 using 声明,则在重载决策期间,所有候选函数在其隐式对象中具有相同的类类型范围。但这对上面的例子意味着什么?候选人会是以下吗?

void F1(B&)
void F2(C&)
// call arguments: (lvalue D)

这似乎是错误的,因为我们在查找结果集中只有一个声明10.2p7。我们该如何解释这个??

4

4 回答 4

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 )。

首先 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,问题中提出的情况根本不存在。

于 2011-04-16T01:30:31.627 回答
0

我认为关键在于 10.2p5,其中的标准是指检查每个“直接基类子对象”。

因为A是虚拟继承的,所以是D(10.1p4)的“直接基类子对象”。

然后考虑 的三个子对象DABC。由10.2p6BC淘汰(A是基数这些),只有A::f一个候选。

于 2011-04-16T17:38:41.053 回答
0

我不是直接解决这个问题,而是试图争辩说,假装f每个派生类中都存在函数是行不通的:

只有一个候选函数,它有类型

void A::(void)

虽然你可以形成一个指向该函数的成员指针

void (A::*F0)(void) = &A::f;
void (B::*F1)(void) = F0;
void (C::*F2)(void) = F0;

这是因为指向成员的指针包含计算函数参数所需的附加信息。指向成员的调用站点找到实际目标实例的子A对象,以提供. 函数内部没有从派生类型的指针中查找 A 的成员的逻辑。所以一个人不能谈论,正如你的问题所暗示的那样。thisfthisvoid F1(B* this)void F2(C* this)

如果函数被认为是派生类的成员,则为

void B::A::f(void);
void C::A::f(void);

但是由于B::AC::A是同一个基类,最终候选列表中只有一个函数,尽管它在列表中出现了两次。然后虚拟继承提供了两个候选者在同一个对象上调用同一个函数,没有歧义。

于 2011-04-16T04:00:12.350 回答
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)表示查找集。最后提供的句子是必不可少的:由于两者DE具有相同的C基类,并且从 that中E::x 隐藏了,从而使最终的使用明确无误。 现在,在您的示例中,没有什么隐藏 的基类的,所以使用仍然模棱两可,我看不出 10.2p7 如何适用于您的情况。就像楼上说的,完全不确定。;)xCF::x
fDD::f

于 2011-04-16T01:05:56.993 回答