在C++中实现没有虚函数的多重继承

11
我遇到了钻石继承问题,并发现了使用单个钻石解决不同情况的不同方法。然而,我找不到解决“链式”钻石的方法。
根据结构:是的,我想每次都有多个基类,因此虚拟继承不是解决方法(那么它是否仍称为钻石问题?)。我也想避免为钻石的每个中间层编写get/set函数。
p   p
|   |
k   k
 \ /
  s

class parent { int val; };
class kid1 : public parent {};
class kid2 : public parent {};
class school : public kid1, public kid2 {};

现在访问父类中的val的方式如下:

school* s = new school;
s->kid1::val=1; // works

但接下来的“链接”钻石怎么处理呢:

p   p   p   p
|   |   |   |
k   k   k   k
 \ /     \ /
  s       s
  |       |
  c       c
    \   /
      w

class country1 : public school {};
class country2 : public school {};
class world : public country1, public country2 {};

通过以下方式访问val:

world* w = new world;
w->country1::kid1::val=1; // error

结果为:

error: ‘kid1’ is an ambiguous base of ‘world’

为什么?路线到达这个值不是已经明确定义好了吗?


1
@Nikolay:我认为@nem知道这一点。但是对于每个国家,我们都有一个明确的基础kid1。因此问题是,为什么这不起作用? - Armen Tsirunyan
2
我非常确定 school : kid 的关系违反了LSP原则。至少我从来没有去过一个由两个孩子组成的学校 :) - fredoverflow
我担心谁最终负责维护这个代码库的理智 - 它甚至让可怜的Visual Studio发疯了。IDE将其注册为错误,但编译器却没有。 - Jon
这只是假设的情况 :) 我有另一种实现。 - nem
1
@Fred,@Jon:同意,这里使用组合比继承更好。 - Ben Voigt
2个回答

3
并不意味着“来自kid1子对象的val”。它只是一个由包含它的类型(而不是子对象)限定的名称。
我不知道为什么country1::kid1甚至被接受了,但显然它是::kid1的typedef。在world中有两个数据成员都有限定名称::kid1::val
您需要的是:
world* w = new world;
country1* const c1 = world;
c1->kid1::val = 1;

1

没错,这个错误是由于编译器中的一个漏洞造成的。


@Ben Voigt 是哪个规则?编译器找到country1后,它处于限定名称查找(§3.4.3)中,不应该在country1之外查找。 - James Kanze
@James:w->country1::kid1是什么?是一种类型吗?那么w->country1::kid1::val是不明确的,因为该类型是world的两个基类。是一个子对象吗?那么在其后面使用::是非法的。 - Ben Voigt
好问题:-)。显然这是一个类型错误(因为无论如何寻找,kid1都是一个类)。另一方面,自从CFront的时代以来,在C++中这种事情一直有效;如果今天它不起作用,那么标准措辞可能有问题。 - James Kanze
@詹姆斯:如果您能提供一个可以在Comeau下编译的代码示例,我会被说服。但我认为这段代码确实是有歧义的。 - Ben Voigt
@Ben Voigt 我对此的经验可以追溯到Comeau之前;更早的编译器会按照我所描述的方式处理它。但是我最近没有尝试过;随着我的C++技能提高,我发现需要使用它的设计通常不是好的设计。 - James Kanze
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接