C ++ 0x与使用声明混淆

时间:2011-04-16 00:43:54

标签: c++ c++11 overload-resolution using-declaration

这种情况会发生什么:

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我们在查找结果集中只有一个声明。我们该怎么解释这个?

4 个答案:

答案 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)表示查找集。最后提供的句子是必不可少的:由于DE都有相同的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::AC::A是相同的基类,最终候选列表中只有一个函数,尽管它在列表中两次。然后虚拟继承提供了两个候选者在同一个对象上调用相同的函数,没有歧义。

答案 3 :(得分:0)

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

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

然后考虑D的三个子对象:ABC。通过10.2p6 BC被删除(A是这些的基础),只有A::f是候选者。