问题标签 [copy-elision]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
2 回答
171 浏览

c++ - 复制省略和虚拟克隆

在以下场景中如何避免不必要的复制?A 类包含指向大对象的基类型指针。

有时我需要复制对象ob 。所以我实现了虚拟克隆:

但有时我会创建临时的 BigDerivedClass 对象并使用它来构造 A 类:

或者

这里不需要复制创建的对象然后删除它。可以直接在堆中创建此对象并将其地址存储在a.ptr中。

但在我看来,编译器不太可能聪明到在这里实现复制省略(或者是吗?)。那么你会建议什么来避免这种不必要的复制呢?

0 投票
3 回答
3663 浏览

c++ - 如何强制复制省略,为什么它不适用于已删除的复制构造函数?

我有一个不可复制的课程。复制这将是有问题的。我想保证它永远不会被复制,所以我创建了它的复制构造函数deleted

不幸的是,g++ 不会编译这个,原因是:

但这是一个非常明确的情况,应该使用复制省略,因此永远不应该调用复制构造函数。为什么会这样?

0 投票
1 回答
512 浏览

c++ - 保证复制省略在 C++1z 的列表初始化中如何工作?

在 c++ 草案 n4606 [dcl.init] 17.6 中有一段关于保证复制省略:

  • 如果目标类型是(可能是 cv 限定的)类类型:
    • 如果初始化表达式是纯右值并且源类型的 cv 非限定版本与目标类是同一类,则初始化表达式用于初始化目标对象。[示例T x = T(T(T()));调用T默认构造函数进行初始化x。—结束示例]
    • [...]

还有一个问答讨论它是如何工作的。

就我自己的理解而言,我引用的规则保证了当初始化表达式是纯右值并且源类型的 cv 非限定版本与目标类是同一类时,不应该涉及任何 ctor。因此无需检查是否存在复制或移动 ctor,这使得以下代码在 C++17 中是合法的:

然而,让我抓狂的是我在 c++ 草案 n4606 的列表初始化部分找不到类似的规则。我发现的是 ([dcl.init.list] 3.6)

[...]

  • 否则,如果T是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载决议(13.3、13.3.1.7)选择最佳构造函数。如果需要缩小转换(见下文)来转换任何参数,则程序格式错误。[...]

由于列表初始化比我引用的第一条规则具有更高的优先级,因此当初始化程序是初始化程序列表时,我们应该考虑列表初始化部分中的规则。正如我们所见,在列表初始化类类型时会考虑构造函数T。所以,继续前面的例子,将

在 C++17 中合法吗?有人能找到标准草案在哪里指定保证复制省略在列表初始化中如何工作吗?

0 投票
4 回答
188 浏览

c++ - 为什么即使允许发生复制省略,代码也需要具有可访问的复制/移动构造函数?

Nicol Bolas在 SO中的回答中写道:

在许多情况下允许发生复制省略。然而,即使它被允许,代码仍然必须能够像没有删除副本一样工作。也就是说,必须有一个可访问的副本和/或移动构造函数。]

为什么即使允许发生复制省略,代码也有必要(在“保证复制省略”出现之前)维护复制/移动构造函数?

为什么“保证复制省略”将程序员从这些要求中解放出来?

0 投票
1 回答
431 浏览

c++ - 保证复制省略和不可移动{Nonmoveable{}}

我发现 GCC 7 已经实现了保证复制省略,我在wandbox中尝试了以下代码:

我得到了编译错误:

根据cppreference

在初始化中,如果初始化表达式是一个prvalue,并且源类型的cv-unqualified版本与目标类是同一个类,则初始化表达式用于初始化目标对象:
T x = T(T(T())); // only one call to default constructor of T, to initialize x

所以我认为它应该等于const Movable y{};。怎么了?

0 投票
1 回答
885 浏览

c++ - 使用 STL 复制省略(以向量为例)

我正在阅读 C++ 中的复制省略。我对使用这种复制省略的 C++ 中的 STL 有疑问。

以下代码:

所以我在做一个简单的任务(合并)向量A和B(不要注意过程,只关注函数和返回。

使用以下定义,它返回一个vector<int>(无参考)

这也是return result;在函数范围内声明的变量。

我试图查看这些向量所在的内存地址。输出:

两者的内存地址不同,所以我在想只要它不会创建复制构造器,它最后将是相同的内存地址(0xffeffe40)这是否意味着它是复制构造的?

这种方法是复制省略吗?

  • 如果不是:为什么不是复制省略?
  • 如果是:我怎么知道它真的在做复制省略?

还有一个重要的问题:如果这不是复制省略,有可能做到吗?在 STL 中进行复制省略需要什么?

0 投票
1 回答
139 浏览

c++ - 即使使用 -fno-elide-constructors 编译,似乎也会发生复制省略

以下程序的输出是:

为什么这里没有调用移动构造函数?即使我使用标志 -fno-elide-constructors 编译,似乎也会发生复制省略:g++ test.cpp -fno-elide-constructors -std=c++11

0 投票
4 回答
1453 浏览

c++ - 为什么删除临时副本需要移动语义?

所以我对移动语义的理解是,它们允许您覆盖用于临时值(右值)的函数并避免潜在的昂贵副本(通过将状态从未命名的临时移动到命名的左值)。

我的问题是为什么我们需要特殊的语义呢?为什么 C++98 编译器不能忽略这些副本,因为它是编译器确定给定表达式是左值还是右值?举个例子:

即使没有 C++11 的移动语义,编译器仍然应该能够确定传递给的表达式func()是右值,因此不需要从临时对象复制。那么为什么有区别呢?移动语义的这种应用似乎本质上是复制省略或其他类似编译器优化的变体。

再举一个例子,为什么要费心编写如下代码?

似乎const std::string&重载可以处理这两种情况:像往常一样的左值和作为 const 引用的右值(因为临时表达式根据定义是 const 的)。由于编译器知道表达式何时是左值或右值,它可以决定是否在右值的情况下删除副本。

基本上,为什么移动语义被认为是特殊的,而不仅仅是 C++11 之前的编译器可以执行的编译器优化?

0 投票
1 回答
219 浏览

c++ - 结构化绑定和强制复制省略

如果您像这样使用结构化绑定

那么返回的元组中的副本是否会被删除,对象会直接进入ab或者c初始化是否会是来自单个元组元素的移动构造?我怀疑这会导致复制发生,但我不确定标准中对强制复制省略的描述是否能处理这种情况。

0 投票
2 回答
305 浏览

c++ - 标准是否规定副本必须是等效的?

假设我有一个奇怪的字符串类型,它拥有或不拥有它的底层缓冲区:

该复制构造函数是否在某处违反了标准?考虑:

s是拥有还是不拥有取决于附加的复制构造函数是否被省略,这在 C++14 及更早版本中是允许的,但可选(尽管在 C++17 中是保证的)。薛定谔的所有权模型表明这个复制构造函数本身就是未定义的行为。

是吗?


一个更具说明性的例子可能是:

根据被忽略的副本,x.i可能比getX(). 标准对此有任何说明吗?