空对基类的目的是什么?

2024-01-05

libstdc++ 库 https://github.com/gcc-mirror/gcc/blob/16e2427f50c208dfe07d07f18009969502c25dc8/libstdc%2B%2B-v3/include/bits/stl_pair.h#L189pair 的实现有以下奇怪之处

template<typename, typename> class __pair_base
  {
    template<typename T, typename U> friend struct pair;
    __pair_base() = default;
    ~__pair_base() = default;
    __pair_base(const __pair_base&) = default;
    __pair_base& operator=(const __pair_base&) = delete;
  };

template<typename T, typename U>
  struct pair
  : private __pair_base<T, U>
{ /* never uses __pair_base */ };

__pair_base从未使用过,也不能使用,因为它是空的。这尤其令人困惑,因为std::pair is required https://timsong-cpp.github.io/cppwp/pairs#pair-4有条件的结构性

pair<T, U>是一种结构类型,如果T and U都是结构类型。

拥有私人基地使其变得非结构性。


tl;dr 这是一系列非常长的黑客攻击的结果,以实现疯狂的重载/显式规则std::pair并保持 ABI 兼容性。这是 C++20 中的一个错误。


免责声明

这更像是与标准库作者一起沿着记忆之路进行的“有趣”之旅,然后是一些富有洞察力的语言级别启示。它显示了 C++ 的实现变得多么复杂pair,这是一项艰巨的任务。

我尽力重现历史,但我不是作者之一。

配对底漆

std::pair不仅仅是简单的

template<typename T, typename U>
struct pair
{
    T first;
    U second;
};

上面列出了 8 个不同的构造函数参考参数 https://en.cppreference.com/w/cpp/utility/pair/pair,对于实现者来说,甚至更多:每个条件显式构造函数实际上都是two构造函数,一个用于隐式,另一个用于显式。

并非所有这些构造函数都参与重载决策,如果它们参与,到处都会出现歧义。相反,有很多规则来管理每个规则何时执行,并且every上述情况的组合必须由 SFINAE 手动编写和禁用。

多年来,这最终导致了 5 个错误报告单独的构造函数。现在即将成为 6 岁;)

Prologue

The 第一个错误 https://github.com/gcc-mirror/gcc/commit/7b3318c41e6165140e59e026988d1f6e27d01a2a如果类型相同,则短路对参数可转换性的检查。

template<typename T> struct B;
template<typename T> struct A
{
    A(A&&) = default;
    A(const B<T> &);
};

template<typename T> struct B
{
    pair<A<T>, int> a;
    B(B&&) = default;
};

显然,如果他们过早检查可转换性,则由于循环依赖以及如何删除移动构造函数,移动构造函数会被删除。B内仍不完整A.

nonesuch

然而这更改了 SFINAE 属性 https://github.com/gcc-mirror/gcc/commit/f524d5b34aaac95cb4b2ce7126002cd4fa9d5bae of pair。作为回应,实施了另一个修复。此实现启用了以前无效的赋值运算符,因此通过更改其签名来手动关闭赋值运算符

struct nonesuch
{
    nonesuch() = delete;
    ~nonesuch() = delete;
    nonesuch(nonesuch const&) = delete;
    void operator=(nonesuch const&) = delete;
};

// ...
pair& operator=(
    conditional_t<conjunction_v<is_copy_assignable<T>,
                                is_copy_assignable<U>>,
                  const pair&, const nonesuch&>::type)

Where nonesuch是一个虚拟类型,本质上使此重载无法调用。或者是吗?

no_braces_nonesuch

不幸的是,即使你永远无法创建一个nonesuch

pair<int, int> p = {};  // succeeds
p = {};  // fails

你仍然可以用大括号初始化它 https://github.com/gcc-mirror/gcc/commit/c1e2889a320a2e45eb60b6bb7c1d3d8fc0068582. Since delete无法解决重载解析,这是一个硬故障。

解决方法是创建no_braces_nonesuch

struct no_braces_nonesuch : nonesuch
{
    explicit no_braces_nonesuch(const no_braces_nonesuch&) = delete;
};

The explicit关闭参与过载解析。最后,该作业是不可调用的。或者说是……?

__pair_base v1

不幸的是,有另一种初始化方式 https://github.com/gcc-mirror/gcc/commit/e182158261869320bc6fb1e972fd5a142359965e未知类型

struct anything
{
    template<typename T>
    operator T() { return {}; }
};

anything a;
pair<int, int> p;
p = a;

作者意识到他们可以通过利用默认生成的特殊成员函数“轻松”解决这个问题:如果您有一个不可分配的基数,则根本无法声明它们

class __pair_base
  {
    template<typename T, typename U> friend struct pair;
    __pair_base() = default;
    ~__pair_base() = default;
    __pair_base(const __pair_base&) = default;
    __pair_base& operator=(const __pair_base&) = delete;
  };

所有单元测试都通过了,一切看起来都很光明。不知不觉中,一只邪恶虫子的阴影不祥地出现在地平线上。

__pair_base v2

ABI 崩溃了。

这怎么可能呢?空碱基优化 https://en.cppreference.com/w/cpp/language/ebo他们不是吗?嗯,不。

pair<pair<int, int>, int> p;

不幸的是,空基优化仅适用于基类子对象与相同类型的其他子对象不重叠的情况。在这种情况下,__pair_base内对的一个与外对的一个重叠。

修复很“简单”,我们将其模板化__pair_base以确保它们是不同的类型。

结构类型

C++20 来了,它要求这对是结构类型 https://en.cppreference.com/w/cpp/language/template_parameters。这就要求没有私人基地。

template<pair<int, int>>
struct S;  // fails

我们的旅程就这样结束了。这让我想起Chandler Carruth 在 cppcon 上的快速调查 https://youtu.be/LJh5QCV4wDg?t=2455:“如果需要,谁可以在一年内构建出 C++ 编译器?”考虑到 C++ 的复杂性,只有当前的编译器编写者认为他们可以。显然,我什至不知道如何实施std::pair.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

空对基类的目的是什么? 的相关文章

  • 将类对象放置在向量中?

    我注意到我可以将一个类放置在一个向量中 这是我的程序 我收到以下错误 out blackjack exe blackjack obj blackjack obj error LNK2019 unresolved external symbo
  • Rx.NET 中是否有一个Subject 实现,其功能类似于BehaviourSubject,但仅在值发生更改时才发出?

    有没有Subject https learn microsoft com en us previous versions dotnet reactive extensions hh229699 v vs 103 Rx NET 中的实现在功能
  • 转换 const void*

    我有一个函数返回一个const void 我想用它的信息作为char 我可以将它投射为 C 风格的罚款 char variable但是当我尝试使用reinterpret cast like reinterpret cast
  • 未找到 Boost 库,但编译正常

    我正在尝试在 C 中使用 boost 的文件系统 使用时看起来编译没问题 c c Analyse c o Analyse o g W Wall L usr local lib lboost filesystem lboost system
  • 有些有助于理解“产量”

    在我不断追求少吸的过程中 我试图理解 产量 的说法 但我不断遇到同样的错误 someMethod 的主体不能是迭代器块 因为 System Collections Generic List 不是迭代器接口类型 这是我被卡住的代码 forea
  • 处理右值时的 insert 与 emplace

    std string myString std unordered set
  • 不同 C++ 文件中的相同类名

    如果两个 C 文件具有相同名称的类的不同定义 那么当它们被编译和链接时 即使没有警告也会抛出一些东西 例如 a cc class Student public std string foo return A void foo a Stude
  • 在 VS 中运行时如何查看 C# 控制台程序的输出?

    我刚刚编写了一个名为 helloworld 的聪明程序 它是一个 C NET 4 5 控制台应用程序 在扭曲的嵌套逻辑迷宫深处 使用了 Console WriteLine 当我在命令行运行它时 它会运行并且我会看到输出 我可以执行其他命令并
  • 是否使用 C# 数据集? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我对 C 中的数据集概念有点困惑 编码 ASP NET 站点 但这并不重要 在我的阅读中 我了解到它们 本质上 用作我的应用程序和我的
  • 如何将AVFrame转换为glTexImage2D使用的纹理?

    如您所知 AVFrame 有 2 个属性 pFrame gt data pFrame gt linesize 当我从视频 sdcard test mp4 android平台 读取帧后 并将其转换为RGB AVFrame副 img conve
  • 已发布的 .Net Core 应用程序警告安装 .Net Core,但它已安装

    我制作了一个 WPF 和控制台应用程序 供某人在我无法访问的私人服务器上使用 我使用 Visual Studio 2019 的内置 发布向导 来创建依赖于框架的单文件应用程序 当该人打开 WPF 应用程序时 他们会看到标准警告 他们单击 是
  • 在 C 中使用枚举而不是 #defines 作为编译时常量是否合理?

    在 C 工作了一段时间后 我将回到 C 开发领域 我已经意识到 在不必要的时候应该避免使用宏 以便让编译器在编译时为您做更多的工作 因此 对于常量值 在 C 中我将使用静态 const 变量或 C 11 枚举类来实现良好的作用域 在 C 中
  • 将二变量 std::function 转换为单变量 std::function

    我有一个函数 它获取两个值 x 和 y 并返回结果 std function lt double double double gt mult double x double y return x y 现在我想得到一个常量 y 的单变量函数
  • 将函数参数类型提取为参数包

    这是一个后续问题 解包 元组以调用匹配的函数指针 https stackoverflow com questions 7858817 unpacking a tuple to call a matching function pointer
  • C++ 对象用 new 创建,用 free() 销毁;这有多糟糕?

    我正在修改一个相对较大的 C 程序 不幸的是 并不总是清楚我之前的人使用的是 C 还是 C 语法 这是在一所大学的电气工程系 我们 EE 总是想用 C 来做所有事情 不幸的是 在这种情况下 人们实际上可以逃脱惩罚 但是 如果有人创建一个对象
  • 模板类中的无效数据类型生成编译时错误?

    我正在使用 C 创建一个字符串类 我希望该类仅接受数据类型 char 和 wchar t 并且我希望编译器在编译时使用 error 捕获任何无效数据类型 我不喜欢使用assert 我怎样才能做到这一点 您可以使用静态断言 促进提供一个 ht
  • C++:二叉树所有节点值的总和

    我正在准备面试 我被一个二叉树问题困住了 我们如何计算二叉树所有节点中存在的值的总和 优雅的递归解决方案 伪代码 def sum node if node NULL return 0 return node gt value sum nod
  • 在 Win32 控制台应用程序中设置光标位置

    如何在 Win32 控制台应用程序中设置光标位置 最好 我想避免制作句柄并使用 Windows 控制台功能 我花了整个早上沿着那条黑暗的小巷跑 它产生的问题比它解决的问题还要多 我似乎记得当我在大学时使用 stdio 做这件事相对简单 但我
  • 在 System.Type 上使用条件断点时出错

    这是函数 public void Init System Type Type this Type Type BuildFieldAttributes BuildDataColumns FieldAttributes 我在第一行设置了一个断点
  • 是否允许全局静态标识符以单个 _ 开头?

    换句话说 可能static 文件范围 全局变量恰好以一个下划线开头 而不会产生与 C 实现发生名称冲突的可能性 https www gnu org software libc manual html node Reserved Names

随机推荐

  • 线程实现

    我想知道如何实施我自己的线程库 我拥有的是CPU PowerPC架构 和C标准库 有没有我可以看看的开源轻量级实现 最简单的线程需要 一些内存用于堆栈空间 存储其上下文的地方 即寄存器内容 程序计数器 堆栈指针等 最重要的是 您需要实现一个
  • 启用 mod_deflate 发送 Content-Encoding: gzip

    EDIT我发现问题实际上是PHP 缩小 http code google com p minify 这是发送压缩的内容而不是 Apache 我会找到更多这方面的信息 根据高性能网站 http oreilly com catalog 9780
  • F# 命令行文件顺序?

    为什么参数的顺序对于 F 来说很重要 对于 C 使用相同的编译模型 来说并不重要 当我尝试这个时 main fs module Main let main Printer print repeatedly 5 hello world pri
  • Android studio 2.2.0-rc1 gradle插件错误

    我刚刚将 Android Studio 更新到该版本2 2 0 rc1安卓得到错误 找不到 com android tools build gradle 2 2 0 rc1 在以下位置进行了搜索 https jcenter bintray
  • 将gradle依赖添加到库aar包中

    我正在为供应商制作一个库项目 它需要 Android Volley 作为依赖项 我用过这个在Android Studio中创建aar文件 https stackoverflow com questions 24309950 create a
  • 如何使用 Jest 测试 useParam() 函数

    Getting this error while testing a component having a hook useParam I fixed it using below code jest mock react router d
  • 无法将名称“repository:repositories”解析为“类型定义”组件

    我在编写 applicationContext xml 时遇到异常 org springframework beans factory xml XmlBeanDefinitionStoreException Line 18 in XML d
  • OpenCV + GigE 视觉相机 +c++

    几天以来 我开始研究 Mako 相机 使用以太网 GigE 我需要取回视频流以将其集成到软件公司中 我尝试在 code blocks 上使用 OpenCV 库 但总是出现错误 指出它无法看到视频流 有人可以帮助我吗 非常感谢你的帮助 inc
  • Tkinter 方法执行后立即

    TKinter after 方法立即执行 执行后暂停 3 秒 如果我还在 CheckStatus 函数中使用 after 方法 它将进入快速循环并且永远不会到达 mainloop 我究竟做错了什么 文档说该函数将在暂停时间之后调用 但实际上
  • 修改Vite/Rollup中资产的构建路径?

    假设我有这个文件夹结构 parent parent html parent js child child html child js 我希望它们在我的 dist 文件夹中以相同的结构输出 默认情况下 这是获取输出的内容 dist asset
  • 如何使用 jQuery 获取具有特定 CSS 属性的第一个父级?

    我需要找到第一个拥有的父母position relative 类似于下面的示例 但我的真实内容将动态生成 div div div div div div 您知道使用 jQuery 执行此操作的简单方法吗 You can filter的集合p
  • iOS 5 中的表情符号和 UIWebView

    我注意到我的应用程序中的表情符号已停止在 iOS 5 中的 UIWebView 上正确显示 所有字符在显示时都会被编码为 HTML 并且输出 HTML 为 p Emoji iOS 4 55357 56850 p 此 UTF 8 编码的 HT
  • ipv6 向后兼容 ipv4 吗?

    我有一个使用 ipv4 编写的小 udp 示例程序 如果我将代码更改为 ipv6 我仍然能够与使用具有 ipv4 地址的侦听器的任何人进行通信吗 我正在查看移植示例 http ou800doc caldera com en SDK neta
  • 如何将 ZeroMQ 套接字与 Ratchet Web-socket 库绑定以实现 PHP 应用程序的实时应用程序?

    我只是涉及 websocket Ratchet 和 ZeroMQ 的整个领域的初学者 以我的基本理解 websocket有助于在服务器和客户端之间创建开放连接 Ratchet是一个基于 PHP 的库 它使用 PHP 的核心 Socket 函
  • 有人知道 Java 的邮件(SMTP)传递库吗?

    我想发送邮件而不用担心用于投递的 SMTP 服务器 So Java邮件API http java sun com products javamail 对我不起作用 因为我必须指定要连接的 SMTP 服务器 我希望图书馆通过查询邮件地址域的
  • Spring Data Rest PUT 与 PATCH LinkableResources

    我正在使用 Spring Data REST 来公开我的实体及其关系 我在两个实体之间有一对一的关系 并且我正在尝试更新 更改与 PUT 和 PATCH 的关系 我注意到 Spring Data REST 只允许您更新链接资源 JPA 映射
  • 检测IE9而不进行功能检测

    所以我需要检测 IE 9 我知道我真的应该使用特征检测 https stackoverflow com questions 1944169 detecting ie using jquery 1944186 1944186但我不知道是什么功
  • 如何通过mvn命令顺序执行2个Java类

    我有 2 个具有共生关系的 Java 类 类 1 生成一些输出文件 类 2 使用类 1 的输出并验证它 这两个类都从命令行获取输入 这个项目是基于maven的 鉴于这种共生性质 我不确定如何 连接它们 我的想法是 编写另一个 Java 类
  • exec:语法错误:“返回”外部函数

    我将代码片段存储在 Postgres 数据库中 当我需要代码时 我在数据库中找到它并使用exec 功能 代码片段是extract功能 不幸的是它返回了SyntaxError return outside function Method de
  • 空对基类的目的是什么?

    libstdc 库 https github com gcc mirror gcc blob 16e2427f50c208dfe07d07f18009969502c25dc8 libstdc 2B 2B v3 include bits st