不死对象 ([basic.life]/8):为什么允许引用重新绑定(和 const 修改)?

2024-03-28

“不死”条款

我将 undead 子句称为 C++ 规则,即在对象销毁后,如果在同一地址创建新对象,则有时可以将其视为与旧对象相同的对象。该规则在 C++ 中始终存在,但附加条件发生了一些变化。

我被迫阅读最新的不死条款这个问题 https://stackoverflow.com/q/59239085/963864。修订后的条件Lifetime [基本生活]/8 https://stackoverflow.com/q/59239085/963864 are:

(8.1) 新对象的存储完全覆盖存储 原始对象占据的位置,以及

嗯,呃。不同地址的对象不会是同一个对象。

(8.2) 新对象与原对象类型相同 (忽略顶级简历限定符),以及

再说一次,呃。

(8.4) 原始对象和新对象都不是 可能重叠的子对象([intro.object])。

它不能是基类、经典类(或具有使其地址不唯一的特殊声明的成员)。再说一次,呃。

(8.3) 原始对象既不是一个完整的对象, const 限定的也不是此类对象的子对象,并且

现在这很有趣。被替换的对象不能是:

  • 一个完整的 const 对象
  • 完整 const 对象的一部分

另一方面,被复活的对象可以是:

  1. const 成员子对象
  2. 此类常量成员的子对象
  3. const 对象数组中的元素

常量子对象

所以在我看来所有这些物体x可以复活:

常量成员子对象

struct CI {
  const int x;
};

CI s = { 1 };
new ((void*)&s.x) int(2);
int r = s.x; // OK, 2

const 成员的子对象:

struct T {
  int x;
};

struct CT {
  const T m = { 1 };
};

CT s;
new ((void*)&s.m.x) int (2);
int r = s.m.x;

const 对象数组中的元素:

const int x[1] = { 1 };
new ((void*)&x[0]) int (2);
int r = x[0];

具有 const 和引用成员的类

此外,具有 const 或引用成员的类类型对象似乎也不被禁止;复活的对象仍然被称为x.

具有 const 成员的类:

struct CIM {
  CIM(int i): m(i) {}
  const int m;
};

CIM x(1);
new ((void*)&x) CIM(2);
int r = x.m; // OK, 2

具有参考成员的类:

struct CRM {
  CRM (int &r): m(r) {}
  int &m;
};

int i=1,j=2;
CRM x(i);
new ((void*)&x) CRM(j);
int r = x.m; // OK, 2

问题

  1. 该条款的这种解释正确吗?
  2. 如果是的话,是否还有其他条款禁止这些覆盖操作?
  3. 如果是这样,这是故意的吗?为什么会改变?
  4. 这对代码生成器来说是一个重大改变吗?所有编译器真的都支持吗?他们不是基于 const 成员、数组的 const 元素不可变和引用不可反弹来进行优化吗?
  5. 额外问题:这是否会影响具有足够存储类(当然不是动态创建的对象)和足够初始化的 const 对象的 ROM 能力?

注意:我后来添加了这个奖励,因为在讨论中提到了将常量放入 ROM 中。


如果与对象生命周期相关的标准的所有要求都不在[基本生命周期]中,那将是令人惊讶的。

在您引用的标准段落中,“完整”形容词被无意中添加到“对象”名称中的可能性很小。

在论文中P0137 http://wg21.link/p0137r1,人们可以阅读这个理性(下面@LanguageLawyer评论中引用的论文):

这是允许 std::Optional 等类型包含 const 子对象所必需的;现有的限制是为了允许 ROMability,因此只影响完整的对象。

为了让我们放心,我们可以验证编译器确实遵循字母中的标准措辞:它们对完整的 const 对象执行常量优化,但不对非 const 完整对象的 const 成员子对象执行常量优化:

让我们考虑一下这段代码 https://godbolt.org/z/Kt2Cef:

struct A{const int m;};

void f(const int& a);

auto g(){
    const int x=12;
    f(x);
    return x;
}

auto h(){
    A a{12};
    f(a.m);
    return a.m;
}

当面向 x86_64 时,Clang 和 GCC 都会生成此程序集:

g():                                  # @g()
        push    rax
        mov     dword ptr [rsp + 4], 12
        lea     rdi, [rsp + 4]
        call    f(int const&)
        mov     eax, 12     ;//the return cannot be anything else than 12
        pop     rcx
        ret
h():                                  # @h()
        push    rax
        mov     dword ptr [rsp], 12
        mov     rdi, rsp
        call    f(int const&)
        mov     eax, dword ptr [rsp]  //the content of a.m is returned
        pop     rcx
        ret

返回值存放在寄存器中eax(根据 ABI 规范:System V x86 处理器特定 ABI):

  • 在函数中g编译器可以自由地假设x不能通过调用来更改f因为x是一个完整的 const 对象。所以价值12被直接放置在eax注册为立即值:mov eax, 12.

  • 在函数中h编译器不能随意假设a.m不能通过调用来更改f因为a.m不是完整 const 对象的子对象。所以在致电之后f的价值a.m必须从内存加载到eax : mov eax, dword ptr [rsp].

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

不死对象 ([basic.life]/8):为什么允许引用重新绑定(和 const 修改)? 的相关文章

  • MVC Core IActionResult 含义

    什么是IActionResult 我尝试查看 MSDN 和其他网站 但需要通用 常见 易于理解的答案 MSDN IActionResult https learn microsoft com en us dotnet api microso
  • 用 C# 启动 Windows 服务

    我想启动一个刚刚安装的Windows服务 ServiceBase ServicesToRun if bool Parse System Configuration ConfigurationManager AppSettings RunSe
  • 中间件 API 的最佳实践是什么? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我们正在开发一个中间件 SDK 采用 C 和 Java 语言 供游戏开发人员 动画软件开发人员 阿凡达开
  • OpenCV Visual Studio ntdll.dll

    我尝试在 Visual Studio 2013 上使用 OpenCV 2 4 10 创建一个项目 但由于以下异常 到目前为止我运气不佳 请建议帮助 TIA letstryitonemoretime exe Win32 Loaded C Us
  • gtest 和 gmock 有什么区别?

    我试图理解的目的google mock Google 的 C 模拟框架 https github com google googletest blob master googlemock README md 我已经与gtest较早 但我还是
  • CMake 和 Visual Studio:如何获得快速、安静的命令行构建?

    我有一个 cmake 项目 它成功地完成了我想要的一切 但我有大约 100 个文件 当我只需要重新编译一个文件时 我厌倦了每次看到生成的巨大输出 每个文件 30 行 明确地说 我正在编译cmake build 得到这个结果 我需要传递给编译
  • 隐式方法组转换陷阱

    我想知道为什么给定代码的输出 在 LinqPad 中执行 void Main Compare1 Action Main Dump Compare2 Main Dump bool Compare1 Delegate x return x Ac
  • 使用静态类型代替变量

    当您的项目不使用命名空间时 有什么方法可以告诉编译器使用静态类型而不是变量吗 例如 我有一个名为 User 的类 它具有各种静态和非静态方法 假设调用了其中一个静态方法GetUser 我想称之为User GetUser 方法来自一个方法 该
  • 使用 Selenium for C# 登录 Facebook

    我一直在使用 Selenium C 框架并尝试进行 facebook 登录 但没有任何运气 这是我到目前为止得到的 基于这篇文章 使用 Selenium 测试 Facebook Connect 应用程序 https stackoverflo
  • 异步方法中的异常未被捕获

    下面的代码没有捕获我的OperationCancelEException 它是通过调用抛出的ct ThrowIfCancellationRequested public partial class TitleWindow Window IA
  • C++ 私有静态成员变量

    此 C 代码在编译时产生链接器错误 A h class A public static void f private static std vector
  • C 中“for”循环中的两个变量

    我正在编写一些代码 需要在其中使用两个变量for环形 下面的代码看起来没问题吗 它确实给了我预期的结果 for loop 1 offset loop 2 offset 2 loop 1 gt offset 190 loop 2 lt 190
  • 控制器中的异常处理 (ASP.NET MVC)

    当您自己的代码抛出异常并从控制器中的操作调用时 应该如何处理 我看到很多最佳实践的例子 其中根本没有 try catch 语句 例如 从存储库访问数据 public ViewResult Index IList
  • 如何防止字符串被截留

    我的理解 可能是错误的 是 在 C 中 当你创建一个字符串时 它会被实习到 实习生池 中 这保留了对字符串的引用 以便多个相同的字符串可以共享操作内存 但是 我正在处理很多很可能是唯一的字符串 一旦完成每个字符串 我需要将它们从操作内存中完
  • 以标准用户身份打开默认浏览器 (C++)

    我目前正在使用 ShellExecute 打开 在用户浏览器中打开 URL 但在 Win7 和 Vista 中遇到了一些麻烦 因为该程序作为服务运行提升 当 ShellExecute 打开浏览器时 它似乎读取 本地管理员 配置文件而不是用户
  • 在 ncurses 中使用退格键

    我设置了一个简单的 ncurses 程序 它使用 getch 一次读取一个字符并将它们复制到缓冲区中 我遇到的问题是检测到按下退格键 这是相关代码 while buffer i c getch EOF i if c n break else
  • 简单的文档管理系统和API [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Active Directory UserPrincipal.Current.GetGroups() 返回本地组而不是 Web 服务器上的组

    以下内容在我的本地开发盒上效果很好 但是 当我将其移动到网络服务器时 它失败了 甚至不会记录错误 public static List
  • 为什么 std::ranges::filter_view 对象必须是非常量才能查询其元素?

    include
  • 在类中使用 std::chrono::high_resolution_clock 播种 std::mt19937 的正确方法是什么?

    首先 大家好 这是我在这里提出的第一个问题 所以我希望我没有搞砸 在写这篇文章之前我用谷歌搜索了很多 我对编码 C 很陌生 我正在自学 考虑到有人告诉我 只为任何随机引擎播种一次是一个很好的做法 我在这里可能是错的 什么是正确 最佳 更有效

随机推荐