具有多重继承的Using-declaration可以使用msvc编译,但不能使用clang和gcc编译

问题描述 投票:0回答:3
struct A { int i; };
struct B: A {};
struct C: A {};
struct D: B, C {
    using B::i;
    void f() {
        i = 0;
    }
};

似乎我指定使用 A from B 中的 i,但编译器说

错误:“A”是“D”的不明确基数

说实话,我不太明白

using B::i
是做什么的。据我所知,它将
i
从命名空间
B
带到了命名空间
D
。但是,命名空间
B
是否包含
i
,因为它从
i
继承了
A
,还是只有命名空间
A
包含
i
?如果是后者,为什么

//A, B, C is same with above
struct D: B, C {
    void f() {
        B::i = 0;
    }
};

这样编译就没有问题了?
正如您所看到的,我对类继承方面的命名空间的整个概念感到困惑。谁能解释一下命名空间、继承等规则如何与这些示例一起工作?

c++
3个回答
1
投票

using B::i
在那里没有用,因为
i
中没有
B
。它对于函数解析很有用。
i = 0;
this->i = 0;
。由于多重继承,
this->i = 0;
要么是
static_cast<B*>(this)->i = 0;
,要么是
static_cast<C*>(this)->i = 0;
,这会导致歧义。

换句话说,

using B::i
将名称
i
带到了类
D
的直接范围内,并且没有解决歧义。

有用的示例

using

struct A { void f(); };
struct D: A {
    void f(int) { f(); }  // error: no matching function for call to 'D::f()'
};
struct A { void f(); };
struct D: A {
    using A::f;
    void f(int) { f(); }  // ok
};

0
投票

关键字

using
根据上下文有多种含义。 C++ 类作用域在名称解析方面是一个命名空间,但并不完全像一个命名空间——这一点值得注意。在类作用域中使用声明与继承相关,而继承不是命名空间功能。另一方面,类作用域不能通过以后的声明来扩展,而命名空间可以。

命名空间范围中的

using
语句如果在命名空间上使用,则可以是 using-directive,对于命名空间成员以及类型别名和别名模板声明,可以是 using-declaration。在 C++20 中,它可用于枚举。

类作用域中的

using

是一个
using-declaration,它引用派生类定义中的基类成员的名称。这可能允许机会访问模式,例如将基类的受保护成员公开为派生类的公共成员。如果派生类已包含具有相同名称、参数列表和限定条件的成员,则派生类成员将隐藏或覆盖从基类引入的成员。

如果

using-declaration 引用正在定义的类的直接基类的构造函数(例如使用 C::C

),则该基类的所有构造函数都变得可见。

显然,它所引用的名称应该是明确的,但您的示例中并非如此。 D 类包含 A 类两次,一次作为 B 类的基类,一次作为 C 类的基类。它们的完全限定名称为

B::A::i

C::A::i
,缩写为 
B::i
C::i
。现在有一个问题:您的声明中哪些必须隐藏,哪些必须根据上述内容公开? 
C::A::i
 是一个不同的名称,因此 
B::i
 
允许与其冲突。除此之外,由于所有访问权限都默认为 public
,类范围内的任何 using 声明都是多余的。

如果

using

 位于代码块范围内,则其含义与命名空间 using-declaration 相同。

void f() { B::i = 0; }
将与

相同

void f() { using B::i; i = 0; }
假设 B 是一个命名空间。 B 不是命名空间,第二种情况的格式不正确。 QED,类作用域并不完全是命名空间。


-1
投票
我认为这是著名的“钻石问题”的一个很好的例子。当两个父类从同一个祖父类继承,并且两个父类都由单个子类继承时,在多重继承中就会出现这种情况。 你试试这个怎么样

struct A { int i; }; struct B: A { void f() { i = 0; }};
    
© www.soinside.com 2019 - 2024. All rights reserved.