【问题标题】:Copy/move elision requires explicit definition of copy/move constructors复制/移动省略需要明确定义复制/移动构造函数
【发布时间】:2018-09-17 04:58:21
【问题描述】:

考虑以下程序:

#include <iostream>
#include <utility>

class T {
public:
    T() { printf("address at construction:    %zx\n", (uintptr_t)this); }
    // T(const T&) { printf("copy-constructed\n"); } // helps
    // T(T&&) { printf("move-constructed\n"); }      // helps
    // T(const T&) = default;                        // does not help
    // T(T&&) = default;                             // does not help
};

T f() { return T(); }

int main() {
    T x = f();
    printf("address after construction: %zx\n", (uintptr_t)&x);
    return 0;
}

使用g++ -std=c++17 test.cpp 编译会得到以下输出(与clang++ 相同):

address at construction:    7ffcc7626857
address after construction: 7ffcc7626887

基于C++ reference,我希望程序输出两个相等的地址,因为应该保证复制/移动被忽略(至少在 C++17 中)。

如果我明确定义了复制或移动构造函数或两者(参见示例中注释掉的行),程序会给出预期的输出(即使使用 C++11):

address at construction:    7ffff4be4547
address after construction: 7ffff4be4547

简单地将复制/移动构造函数设置为default 没有帮助。

引用明确说明

[复制/移动构造函数]不需要存在或可访问

那么我在这里错过了什么?

【问题讨论】:

标签: c++ c++17 copy-constructor move-semantics copy-elision


【解决方案1】:

因为这是复制省略可能不适用的特殊情况。

引用自[class.temporary] paragraph 3

当类类型 X 的对象被传递给函数或从函数返回时,如果 X 的每个复制构造函数、移动构造函数和析构函数都是平凡的或已删除的,并且 X 至少有一个未删除的复制或移动构造函数,允许实现创建一个临时对象来保存函数参数或结果对象。临时对象分别由函数参数或返回值构造,并且函数的参数或返回对象被初始化,就好像使用未删除的普通构造函数复制临时对象(即使该构造函数不可访问或不会被重载决议选择来执行对象的复制或移动)。 [ 注意:授予此权限以允许将类类型的对象传递给寄存器中的函数或从函数返回。 — 尾注 ]

【讨论】:

  • 有趣的是,在我看来,该标准对这个例外的表述过于宽泛。我提出问题的动机是我的T 相当大而我的堆栈非常小,所以我非常关心不要复制堆栈上的对象。如果这种模式多次出现,向每个类添加显式复制构造函数感觉就像是 hack,我可以在 f() 中做些什么来避免这种情况吗?
  • @AlGebra - 但是您的T 并不大,它非常小。在寄存器中构造并按值返回的非常小的对象可能根本不需要任何堆栈空间。因此,它可能比涉及 RVO 机制更有效,后者至少需要一些间接性。
  • @AlGebra:不要再猜测你的编译器了。如果您的 actual T 确实“相当大”,那么编译器将不会执行此优化。所以让你的编译器完成它的工作,这样你就可以重新开始工作:编写一个正常运行的程序。
  • @BoPersson @NicolBolas 感谢您指出这一点。将const int data[5] = {0}; 添加到T 时,编译器确实忽略了这一举动。然而,在我的真实用例中,对象实际上是在堆栈上重复的,这就是我首先研究这个的原因。在隔离问题时,我一定错过了其他一些相关细节。
猜你喜欢
  • 2023-03-03
  • 2014-01-02
  • 2019-09-29
  • 1970-01-01
  • 2016-06-01
  • 2013-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多