在 C++ 中禁用复制省略

2024-01-12

免责声明:研究的目标是如何禁用所提供代码部分的复制省略和返回值优化。如果想提及诸如 XY 问题之类的问题,请避免回答。该问题具有严格的技术和研究性质,并且以这种方式明确提出

C++14 中引入了复制省略和返回值优化。如果某个对象已在一个表达式中被析构和复制构造,例如复制赋值或按值从函数返回立即值,则复制构造函数将被省略。

以下推理适用于复制构造函数,但对于移动构造函数也可以执行类似的推理,因此不再进一步考虑。

有一些禁用自定义代码复制省略的部分解决方案:

1) 依赖于编译器的选项。对于 GCC,有基于的解决方案__attribule__ or #pragma GCC建筑,像这样https://stackoverflow.com/a/33475393/7878274 https://stackoverflow.com/a/33475393/7878274。但由于它依赖于编译器,因此不会遇到问题。

2)强制禁用复制构造函数,例如Clazz(const Clazz&) = delete。或者将复制构造函数声明为explicit以防止其使用。这种解决方案无法满足任务,因为它改变了复制语义并强制引入自定义名称函数,例如Class::copy(const Clazz&).

3)使用中间类型,就像这里描述的那样https://stackoverflow.com/a/16238053/7878274 https://stackoverflow.com/a/16238053/7878274。由于此解决方案强制引入新的后代类型,因此不会遇到问题。

经过一番研究发现,恢复临时价值可以解决问题。如果将源类重新解释为对此类的单元素数组的引用并提取第一个元素,则复制省略将关闭。模板函数可以这样写:

template<typename T, typename ... Args> T noelide(Args ... args) {
    return (((T(&)[1])(T(args...)))[0]);
}

这种解决方案在大多数情况下效果很好。在以下代码中,它生成三个复制构造函数调用 - 一个用于直接复制赋值,两个用于从函数返回的赋值。它在 MSVC 2017 中运行良好

#include <iostream>

class Clazz {
public: int q;
    Clazz(int q) : q(q) { std::cout << "Default constructor " << q << std::endl; }
    Clazz(const Clazz& cl) : q(cl.q) { std::cout << "Copy constructor " << q << std::endl; }
    ~Clazz() { std::cout << "Destructor " << q << std::endl; }
};

template<typename T, typename ... Args> T noelide(Args ... args) {
    return (((T(&)[1])(T(args...)))[0]);
}

Clazz func(int q) {
    return noelide<Clazz>(q);
}

int main() {
    Clazz a = noelide<Clazz>(10);
    Clazz b = func(20);
    const Clazz& c = func(30);
    return 0;
}

这种方法适用于a and bcase,但使用 case 执行冗余复制c- 应该通过生命周期扩展返回对临时的引用,而不是复制。

Question: 怎么修改noelide模板以允许它与具有生命周期扩展的 const 左值引用一起正常工作? 谢谢!


根据 N4140,2031 年 8 月 12 日:

...

这种复制/移动操作的省略称为复制省略,是 在以下情况下允许(可以组合起来) 消除多个副本):

(31.1) — 在具有类返回类型的函数的 return 语句中, 当表达式是非易失性自动对象的名称时 (函数或 catch 子句参数除外)具有相同的 cv-非限定类型作为函数返回类型,复制/移动 直接构造自动对象可以省略操作 进入函数的返回值

(31.3) — 当一个临时类对象尚未绑定到 引用(12.2)将被复制/移动到具有相同的类对象 cv-非限定类型,复制/移动操作可以省略 将临时对象直接构造到目标中 省略复制/移动

因此,如果我理解正确的话,只有当 return 语句是局部变量的名称时,才会发生复制省略。因此,您可以通过返回例如“禁用”复制省略return std::move(value)...如果您不喜欢使用move为此,您可以简单地实现noelide as a static_cast<T&&>(...).

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

在 C++ 中禁用复制省略 的相关文章

  • 如何获取正在访问 ASP.NET 应用程序的当前用户?

    为了获取系统中当前登录的用户 我使用以下代码 string opl System Security Principal WindowsIdentity GetCurrent Name ToString 我正在开发一个 ASP NET 应用程
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • 动态加载程序集的应用程序配置

    我正在尝试将模块动态加载到我的应用程序中 但我想为每个模块指定单独的 app config 文件 假设我的主应用程序有以下 app config 设置
  • ASP.NET MVC:这个业务逻辑应该放在哪里?

    我正在开发我的第一个真正的 MVC 应用程序 并尝试遵循一般的 OOP 最佳实践 我正在将控制器中的一些简单业务逻辑重构到我的域模型中 我最近一直在阅读一些内容 很明显我应该将逻辑放在域模型实体类中的某个位置 以避免出现 贫血域模型 反模式
  • 查找c中结构元素的偏移量

    struct a struct b int i float j x struct c int k float l y z 谁能解释一下如何找到偏移量int k这样我们就可以找到地址int i Use offsetof 找到从开始处的偏移量z
  • 类模板参数推导 - clang 和 gcc 不同

    下面的代码使用 gcc 编译 但不使用 clang 编译 https godbolt org z ttqGuL template
  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • 创建链表而不将节点声明为指针

    我已经在谷歌和一些教科书上搜索了很长一段时间 我似乎无法理解为什么在构建链表时 节点需要是指针 例如 如果我有一个节点定义为 typedef struct Node int value struct Node next Node 为什么为了
  • 重载<<的返回值

    include
  • WCF 中 SOAP 消息的数字签名

    我在 4 0 中有一个 WCF 服务 我需要向 SOAP 响应添加数字签名 我不太确定实际上应该如何完成 我相信响应应该类似于下面的链接中显示的内容 https spaces internet2 edu display ISWG Signe
  • 什么时候虚拟继承是一个好的设计? [复制]

    这个问题在这里已经有答案了 EDIT3 请务必在回答之前清楚地了解我要问的内容 有 EDIT2 和很多评论 有 或曾经 有很多答案清楚地表明了对问题的误解 我知道这也是我的错 对此感到抱歉 嗨 我查看了有关虚拟继承的问题 class B p
  • 这些作业之间是否存在顺序点?

    以下代码中的两个赋值之间是否存在序列点 f f x 1 1 x 2 不 没有 在这种情况下 标准确实是含糊不清的 如果你想确认这一点 gcc 有这个非常酷的选项 Wsequence point在这种情况下 它会警告您该操作可能未定义
  • WPF/C# 将自定义对象列表数据绑定到列表框?

    我在将自定义对象列表的数据绑定到ListBox in WPF 这是自定义对象 public class FileItem public string Name get set public string Path get set 这是列表
  • 为什么编译时浮点计算可能不会得到与运行时计算相同的结果?

    In the speaker mentioned Compile time floating point calculations might not have the same results as runtime calculation
  • 通过指向其基址的指针删除 POD 对象是否安全?

    事实上 我正在考虑那些微不足道的可破坏物体 而不仅仅是POD http en wikipedia org wiki Plain old data structure 我不确定 POD 是否可以有基类 当我读到这个解释时is triviall
  • 基于 OpenCV 边缘的物体检测 C++

    我有一个应用程序 我必须检测场景中某些项目的存在 这些项目可以旋转并稍微缩放 更大或更小 我尝试过使用关键点检测器 但它们不够快且不够准确 因此 我决定首先使用 Canny 或更快的边缘检测算法 检测模板和搜索区域中的边缘 然后匹配边缘以查
  • 测试用例执行完成后,无论是否通过,如何将测试用例结果保存在变量中?

    我正在使用 NUNIT 在 Visual Studio 中使用 Selenium WebDriver 测试用例的代码是 我想在执行测试用例后立即在变量中记录测试用例通过或失败的情况 我怎样才能实现这一点 NUnit 假设您使用 NUnit
  • IEnumreable 动态和 lambda

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

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

    编辑问题未得到解答 我有一个基于 1 个标准的过滤输出 前 3 个数字是 110 210 或 310 给出 3 个不同的组 从流阅读器控制台 问题已编辑 因为第一个答案是我给出的具体示例的字面解决方案 我使用的实际字符串长度为 450 个

随机推荐