【问题标题】:Is there a way to bring all definitions in a C++ struct/class into scope without deriving from it?有没有办法将 C++ 结构/类中的所有定义都带入范围而不从它派生?
【发布时间】:2020-05-31 12:55:26
【问题描述】:

在周日沉思……

从类派生会将基类中的所有名称都带入派生类的范围。但是,它也会将基类非静态数据成员添加到派生类的每个实例中。

有没有办法在没有后者的情况下实现前者?我问的是简洁的符号。

显然,当基类没有任何数据成员时,我得到了我想要的。在标准库中定义了很多空类或类模板来做到这一点 - 通过继承将名称概括地注入类范围。甚至还有空基类优化,以使其尽可能便宜。

但如果我想对非空基类做同样的事情,我会很想使用类似的东西:

struct Bar {
    using struct Foo;
};

但是,可惜的是,C++ 不支持。我的问题是,是否有另一种方法可以实现我忽略的相同功能?

提供一个更完整的例子:

struct Foo {
    enum { some_constant=42 };

    // data members follow here ...
};

struct Bar {
    using class Foo;   // this doesn't compile

    int f();
};

int Bar::f() {
    return some_constant;   // I want to use the constant directly, without Foo::
}

一种笨拙的方法可能是将 Foo 中的定义分成两个类,一个带有常量(这将是一个我可以从中派生而不会受到惩罚的空类),另一个带有数据成员,但这看起来很像对我来说不优雅。

如果没有一种干净的方法来实现这一点,也许有人可以提供一个理由,说明它为什么不存在,或者不应该存在。

【问题讨论】:

  • 为什么要这样做?
  • 我想达到一个最简洁的符号,特别是当类中定义了许多常量时。
  • 它不存在,因为没有人提出定义它的建议,并通过标准化过程引导它。您将如何增加名称查找规则以适应此功能?它应该只找到枚举值吗? typedef 呢?静态数据成员和成员函数?非静态的?它应该查看using class 指令指定的类的基类吗?进入类那个类以using class 提名?如果名称存在于正确的基类以及using class 指定的基类中,会发生什么?
  • 目前,我假设它会像推导一样工作,只是不将非静态数据成员添加到客户端类中。那会有什么问题吗?当然,根据using class Foo; 行的位置,可以将定义继承为私有、受保护或公共。

标签: c++


【解决方案1】:

从类派生会将基类中的所有名称都带入派生类的范围。

让我阻止你。是的,从基类继承确实会导致基类中的(非private)名称可以从派生类定义中访问。然而,这不是为什么你要从基类继承;这只是继承实现其目标的机制。

从基类公开继承就是对派生类和基类之间的关系作出声明。您是说派生类的每个实例在几乎所有方面都应该表现得像基类。即使virtual 函数覆盖仍然带有这样的期望,即这些方法的派生类实现在概念上执行相同的工作,只是以适合该派生类的方式。

即使是 mixin 风格的基类也是如此,其中基类用于定义导入特定派生类的通用功能。在这样的接口中,几乎不期望用户显式地与基类定义交谈。但是这种通用功能的提供最终仍然基于派生类作为基类的语义思想。这对他们中的许多人来说非常重要。

考虑一下 C++ 标准库中最突出的 mixin:std::enable_shared_from_this<T>。它具有非静态数据成员,没有它们它实际上无法提供它所做的功能(嗯,它可以,但是您必须在派生类中提供一些接口来存储它们,所以它也可以这样做) .

私有继承也是如此,尽管有一些修改。虽然对于外界来说,派生类只是一个派生类,但对于类定义本身来说,它仍然是一个基类。它仍然完全是一个基类,以及随之而来的所有包袱。

不要把机制误认为意义。机制很重要;不要误会我的意思。但这些机制的存在是为了促进意义

让一个类包含其他类的所有内容除了非静态数据成员在语义上是无意义的。这并不意味着类型之间的关系。而且您基本上已经承认,您想要这样做的主要原因是您不必对“基”类中定义的名称进行范围限定。

这是一个机械原因,而不是语义原因。您不应该使用继承之类的语义工具来逃避您选择设计类型的方式所带来的机械后果。

【讨论】:

  • 这是一个很好的答案,但它仍然不能解决我的问题。我可能一直在尝试滥用继承,但这只是因为我找不到更好的方法来给我想要的东西。我没有迷上它,如果确实有更好的方法,我完全赞成。但是到目前为止,所有这些替代方案都涉及将定义分成两部分,这也使它们在源中物理上分开,即使它们当然可以存在于同一个文件中。但是,如果必须如此,那就应该如此。不是最佳的,但我可以忍受它。还是谢谢!
【解决方案2】:

参考您的具体示例,您可以将要访问的常量设为静态,这将允许您通过完全限定“基”类从第二类访问它们

【讨论】:

  • 作为一个激励性的例子,考虑 struct Foo 定义微控制器中硬件外围设备的寄存器集,struct Bar 将是该外围设备的设备驱动程序的定义。 Foo 永远不会被实例化,因为寄存器集存在于硬件中。然而,Bar 会被实例化,并且需要 Foo 中的定义。
  • 我不得不承认,您的用例超出了我自己的经验,我正在努力描绘代码中的样子:希望您不介意我问一些问题。在您的上下文中,您想要继承哪些功能,以及您不想继承哪些数据?我想我要解决的最终问题是:为什么你的“子”类中需要的功能不能在一个单独的类中实现,这两个类都派生自?或者更确切地说,你觉得它有什么笨拙/笨拙的地方?
  • 我想继承的主要是常量,而不是函数。在激励示例中,这将是寄存器位和位域的位掩码,以及要放入其中的值的定义。其中绝大多数可以定义为枚举值。分成两个类确实是最接近我需要的解决方案,但我不得不拆分感觉很奇怪,因为常量与寄存器密切相关,所以我的感觉是定义属于一起。跨度>
  • @sh-:在我看来,这个问题与任何语义无关。也就是说,继承实际上意味着什么。它表达了两种类型之间的关系。您要做的只是使语法对您更方便,以删除您认为不必要的限定符。这就是 namespaces 的用途,用于表示多个定义的逻辑分组。
  • @sh-: "如果我定义了一个表示硬件寄存器集的结构,并且常量与这些寄存器中的位有关,为什么它们不属于一起呢? " 他们确实属于同一个人。我不明白为什么“在一起”对你来说意味着“在那个班级”。在 C++11 之后,基本上没有充分的理由将枚举放在一个类中,除非您想通过将枚举设为私有来隐藏枚举。将它们放在同一个类中可以获得什么,而将它们放在同一个 header 中无法获得什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-24
  • 1970-01-01
  • 1970-01-01
  • 2017-12-04
  • 2021-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多