空对基类的目的是什么?

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(使用前将#替换为@)

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

  • 没有强命名的代码签名是否会让您的应用程序容易被滥用?

    尝试了解authenticode代码签名和强命名 我是否正确地认为 如果我对引用一些 dll 非强命名 的 exe 进行代码签名 恶意用户就可以替换我的 DLL 并以看似由我签名但正在运行的方式分发应用程序他们的代码 假设这是真的 那么您似
  • WCF RIA 服务 - 加载多个实体

    我正在寻找一种模式来解决以下问题 我认为这很常见 我正在使用 WCF RIA 服务在初始加载时将多个实体返回给客户端 我希望两个实体异步加载 以免锁定 UI 并且我想利用 RIA 服务来执行此操作 我的解决方案如下 似乎有效 这种方法会遇到
  • 在哪里可以找到列出 SSE 内在函数操作的官方参考资料?

    是否有官方参考列出了 GCC 的 SSE 内部函数的操作 即 头文件中的函数 除了 Intel 的 vol 2 PDF 手册外 还有一个在线内在指南 https www intel com content www us en docs in
  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • 为什么当实例化新的游戏对象时,它没有向它们添加标签? [复制]

    这个问题在这里已经有答案了 using System Collections using System Collections Generic using UnityEngine public class Test MonoBehaviou
  • 用于登录 .NET 的堆栈跟踪

    我编写了一个 logger exceptionfactory 模块 它使用 System Diagnostics StackTrace 从调用方法及其声明类型中获取属性 但我注意到 如果我在 Visual Studio 之外以发布模式运行代
  • 将 VSIX 功能添加到 C# 类库

    我有一个现有的单文件生成器 位于 C 类库中 如何将 VSIX 项目级功能添加到此项目 最终目标是编译我的类库项目并获得 VSIX 我实际上是在回答我自己的问题 这与Visual Studio 2017 中的单文件生成器更改 https s
  • C# 中通过 Process.Kill() 终止的进程的退出代码

    如果在我的 C 应用程序中 我正在创建一个可以正常终止或开始行为异常的子进程 在这种情况下 我通过调用 Process Kill 来终止它 但是 我想知道该进程是否已退出通常情况下 我知道我可以获得终止进程的错误代码 但是正常的退出代码是什
  • C#中如何移动PictureBox?

    我已经使用此代码来移动图片框pictureBox MouseMove event pictureBox Location new System Drawing Point e Location 但是当我尝试执行时 图片框闪烁并且无法识别确切
  • 如何设计以 char* 指针作为类成员变量的类?

    首先我想介绍一下我的情况 我写了一些类 将 char 指针作为私有类成员 而且这个项目有 GUI 所以当单击按钮时 某些函数可能会执行多次 这些类是设计的单班在项目中 但是其中的某些函数可以执行多次 然后我发现我的项目存在内存泄漏 所以我想
  • 什么时候虚拟继承是一个好的设计? [复制]

    这个问题在这里已经有答案了 EDIT3 请务必在回答之前清楚地了解我要问的内容 有 EDIT2 和很多评论 有 或曾经 有很多答案清楚地表明了对问题的误解 我知道这也是我的错 对此感到抱歉 嗨 我查看了有关虚拟继承的问题 class B p
  • 使用 x509 证书签署 json 文档或字符串

    如何使用 x509 证书签署 json 文档或字符串 public static void fund string filePath C Users VIKAS Desktop Data xml Read the file XmlDocum
  • 如何使用 C# / .Net 将文件列表从 AWS S3 下载到我的设备?

    我希望下载存储在 S3 中的多个图像 但目前如果我只能下载一个就足够了 我有对象路径的信息 当我运行以下代码时 出现此错误 遇到错误 消息 读取对象时 访问被拒绝 我首先做一个亚马逊S3客户端基于我的密钥和访问配置的对象连接到服务器 然后创
  • 如何从两个不同的项目中获取文件夹的相对路径

    我有两个项目和一个共享库 用于从此文件夹加载图像 C MainProject Project1 Images 项目1的文件夹 C MainProject Project1 Files Bin x86 Debug 其中有project1 ex
  • 为什么编译时浮点计算可能不会得到与运行时计算相同的结果?

    In the speaker mentioned Compile time floating point calculations might not have the same results as runtime calculation
  • 基于 OpenCV 边缘的物体检测 C++

    我有一个应用程序 我必须检测场景中某些项目的存在 这些项目可以旋转并稍微缩放 更大或更小 我尝试过使用关键点检测器 但它们不够快且不够准确 因此 我决定首先使用 Canny 或更快的边缘检测算法 检测模板和搜索区域中的边缘 然后匹配边缘以查
  • 混合 ExecutionContext.SuppressFlow 和任务时 AsyncLocal.Value 出现意外值

    在应用程序中 由于 AsyncLocal 的错误 意外值 我遇到了奇怪的行为 尽管我抑制了执行上下文的流程 但 AsyncLocal Value 属性有时不会在新生成的任务的执行范围内重置 下面我创建了一个最小的可重现示例来演示该问题 pr
  • 是否可以在 .NET Core 中将 gRPC 与 HTTP/1.1 结合使用?

    我有两个网络服务 gRPC 客户端和 gRPC 服务器 服务器是用 NET Core编写的 然而 客户端是托管在 IIS 8 5 上的 NET Framework 4 7 2 Web 应用程序 所以它只支持HTTP 1 1 https le
  • IEnumreable 动态和 lambda

    我想在 a 上使用 lambda 表达式IEnumerable
  • Windows 和 Linux 上的线程

    我在互联网上看到过在 Windows 上使用 C 制作多线程应用程序的教程 以及在 Linux 上执行相同操作的其他教程 但不能同时用于两者 是否存在即使在 Linux 或 Windows 上编译也能工作的函数 您需要使用一个包含两者的实现

随机推荐

  • 线程实现

    我想知道如何实施我自己的线程库 我拥有的是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