【问题标题】:Non-const copy constructor compiles fine with C++17非常量复制构造函数可以用 C++17 编译
【发布时间】:2021-07-08 09:29:39
【问题描述】:

我想知道为什么下面的代码不能用 C++14 编译,但用 C++17 编译得很好。自 C++17 以来有什么可以改变的想法吗?这当然是关于 A 类的非常量复制构造函数。我使用的是 VS 2019。这段代码是否有效?

class A {
public:
    A() { }
    A(A& a) { }
};

A fun() {
    A a;
    return a;
}

int main()
{
    A a = fun();
}

来自编译器的消息:

  1. A 类没有合适的复制构造函数
  2. 初始化无法从 A 转换为 A
  3. 由于复制构造函数不明确或没有可用的复制构造函数,无法复制构造类 A

【问题讨论】:

  • 一个问题是 MSVC 长期以来都有一个扩展,允许临时对象被非常量引用绑定。
  • 可能这是因为mandatory copy elision自C++17起
  • @dewaffled 我不这么认为,因为这是 NRVO 的情况,其中复制省略不是强制性的。
  • 请在问题中包含错误信息
  • @Jodocus 复制省略不仅是 return 语句中的东西,初始化对象时有关它的规则也在 c++17 中更新。这就是这里相关的部分。

标签: c++ c++17 c++14 language-lawyer copy-constructor


【解决方案1】:

fun()A类型的prvalue,所以A a = fun();表示a是函数调用的结果对象,没有中间临时对象。

这方面的文字是 C++17 [basic.lval]/2:

prvalue的结果对象是初始化的对象 按prvalue;

A a = A(A(A(A(A(fun()))))); 等也是一样的——所有纯右值都以a 作为结果对象。

return语句的行为在[stmt.return]/2:

return 语句初始化(显式或隐式)函数的 glvalue 结果或 prvalue 结果对象 从操作数通过复制初始化 (11.6) 调用。

结果对象可以通过来自afun的局部变量)的复制初始化成功初始化,因为这是一个非常量左值,因此采用非常量左值引用的复制构造函数确实绑定到它.


在 C++17 之前,fun() 的返回值是一个临时对象,然后 main 的 a 是从临时对象复制/移动构造的,省略是可选的(但需要存在有效的构造函数)。

【讨论】:

  • return 语句初始化(显式或隐式)函数调用的 glvalue 结果... 与 [expr.call]/9 一起说 函数调用的结果是return 语句的可能转换操作数的结果,这意味着return 语句从其操作数初始化其操作数(其结果)。这就是我们喜欢 C++ 的原因。
  • @LanguageLawyer 是的,我认为 [stmt.return]/2 中的那句话有点缺陷,但意图似乎很明确
猜你喜欢
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 2010-10-01
  • 2021-09-20
  • 1970-01-01
  • 2013-05-02
  • 1970-01-01
相关资源
最近更新 更多