首页 > 解决方案 > C++20 是否为继承类模板的公共成员不可见和源代码膨胀问题提供了任何新的解决方案?

问题描述

C++20 是否提供任何新的解决方案来解决公共成员不可见和源代码膨胀/重复与2 年前这个问题中描述的继承类模板的问题?

标签: c++templatesc++20

解决方案


问题”

所谓的“问题”是,在模板中,以不依赖于专业化的方式使用的非限定名称真正独立于专业化,并且指的是在该点找到的具有该名称的实体。所谓的源代码“膨胀”this->用于明确地使名称依赖或限定名称。这仍然是 C++20 中的情况。

为了清楚起见,实体集在我们引用它们时是未知的。在链接的问题中,基类依赖于模板参数,而我们只看到了初级基类模板。基类模板可能会在以后专门化,并且可能具有与我们看到的完全不同的成员函数。因此,任何“解决方案”都需要一个名称没有任何明显的上下文依赖于专业化,才能找到尚未声明的实体,这可能令人惊讶

为什么不可能

在这个方向上的任何天真的变化要么相当大,要么有严重的缺点,或者两者兼而有之。

您可以将所有名称查找推迟到实例化时间,放弃两阶段查找。这会导致ODR 违规,这是无声的 UB,这是一个巨大的缺点。

您可以限制特化,以便以后不能特化基类,从而找到不同的实体。这很难诊断,因此很可能是引入无声 UB 的新规则。

您可以选择使用 using 声明:using X::*按照您的建议或using class X其他人在不同上下文中建议的方式。这具有明确性的好处。它将问题更上一层楼:如果 X 不依赖,大概现在应该找到它,但如果它是依赖的,会发生什么?我们不能在实例化我们现在所在的模板之前实例化它。因此,在实例化之前我们无法解释我们看到的任何名称。它与丢弃两阶段查找具有相似的缺点。

这些更改中的任何一个都会增加已经很复杂的领域的复杂性,并且还会造成重大的向后兼容性障碍。他们都不是一个明显的胜利。

注意:在 C++20 中,它们确实通过允许 ADL 查找具有指定参数的函数模板来使规则更加统一:f<int>(1).

为什么这不是问题

我怀疑是否会达成共识,认为这确实是一个问题。链接的问题提出了一个糟糕的论点。派生类将成员函数行为添加到某个基类,但自由函数效果更好。这些行为不需要成员访问,语言不要求它们是成员,它们可以作为 ADL 的非成员找到,并且通过使用自由函数,即使您拥有的静态类型是基类型,它们也适用. 因此,为此使用继承是不必要的耦合,并且是一个更糟糕的选择。

当我必须模板化一个基类时,搜索 100 多个地方来添加这个 - > 并添加这 6 个字符 100 次让我觉得代码膨胀和重复

“搜索”:当找不到名称时,编译器会告诉您,这比无声的不良行为要好,例如使 ODR 违规更容易被命中。

“模板化基类”:模板化基类不会触发此操作。模板化派生类并使基类依赖。是的,在对派生类进行模板化时,指定以独立于模板参数的方式使用的裸名称实际上是依赖的,对某些人来说可能看起来像样板文件,但其他人可能会认为显式更清晰。

“100s of times”:似乎是双曲线的。

这些代码模式在现实世界中一直使用。看看CRTP。(对链接问题的评论)

同样,这仅适用于派生类模板化的情况。我会质疑共性,但这些成语确实存在并且占有一席之地。

不过,最重要的是,CRTP 不是目标。CRTP 是一个黑客。这是一个 C++ 习惯用法,因为 C++ 缺乏更好的工具。CRTP 允许一个类选择某些行为,否则这些行为编写起来会很麻烦。确实存在相关的 C++ 提案,但总的来说,他们专注于使扩展更容易或删除样板,而不是使 CRTP,即 hack,更容易。

这些是我想到的一些:

C++20:比较

CRTP 的一个非常常见的用途是用于需要大量额外样板的事情。例如, C++ 要求您定义operator==and operator!=。通过选择加入 CRTP 基类,可以只定义原始操作并生成另一个操作。

C++20修复了比较的潜在问题。可以默认典型的成员比较,并且可以重写比较,以便!=可以调用==.

问题是从根本上解决的,删除 CRTP,而不是增强它。

C++20:范围的迭代器

与上述相同,CRTP 的另一个常见用途是迭代器。编写自定义迭代器只需要一些基本操作:前向迭代器的提前和取消引用。但是还有很多额外的看似不必要的仪式:pre-increment、post-increment、const iterators、typedefs 等。

C++20 通过引入范围概念和范围库向前迈出了一大步。结果是编写自定义迭代器的必要性大大降低。范围本身就成为一个有能力的概念,并且有一套很好的范围组合器。

C++20:概念

C++ 本质上有两个系统来指定接口:虚函数和概念。他们有取舍。虚函数是侵入性的。但在 C++20 之前,概念是隐含的和模拟的。使用 CRTP 或继承的一个原因通常是注入虚函数。但是在 C++20 中,概念是一种语言特性,可以消除一个很大的负面影响。

未来的 C++:元类

除了样板减少之外,CRTP 的一个价值是满足多种类型要求的整个集合。一个可比较的类定义了所有的比较运算符。可克隆基定义了虚拟析构函数和克隆。

这是元类的主题,C++ 中还没有。

也可以看看

另见关于定制点的工作,这似乎很有趣。并查看关于统一函数调用语法的辩论,这似乎我们永远不会得到。

概括

这里隐藏了一个很好的问题,关于 C++20 如何更容易减少样板代码、删除 CRTP 之类的 hack 以及编写更好、更清晰的代码。C++20 在这方面采取了几个步骤,但它们使意图的表达更容易,而不是特定的习语。


推荐阅读