使用复制列表初始化从函数返回,不需要复制/移动构造函数 - C++ 11 标准中在哪里说明?

Posted

技术标签:

【中文标题】使用复制列表初始化从函数返回,不需要复制/移动构造函数 - C++ 11 标准中在哪里说明?【英文标题】:Return from function using copy-list-initialization, no copy/move constructor needed - Where's it stated in C++ 11 standard? 【发布时间】:2021-01-26 08:20:43 【问题描述】:
struct A 
  A() 
  A(const A&) = delete;
;

A foo() 
  return ; // Calls A()


struct B 
  B(const B&) = delete;
;

B bar() 
  return ; // Aggregate initialization.


foobar 在 C++11 中都可以正常编译,因为它们使用复制列表初始化。不需要复制省略。

C++ 标准中哪里提到在这种情况下不需要复制/移动构造函数?

我可以在 [stmt.return] 中看到 带有花括号初始化列表的 return 语句从指定的复制列表初始化 (8.5.4) 初始化要从函数返回的对象或引用初始化列表。

我找不到提到在这种情况下不需要复制/移动构造函数的部分。

【问题讨论】:

【参考方案1】:

这是一个 C++11(以及 14 和 17)问题,允许 聚合初始化 绕过复制构造函数检查。

B 是一个聚合类(在 C++11/14/17 中) 您可以在 C++17 中使用 std::is_aggregate 类型特征验证这一点 您正在使用list-initialization 这会执行聚合初始化,其处理方式与常规构造函数略有不同 根据 @NicolBolas' nice answer 关于 [stmt.return],return 就像直接对返回的对象执行复制列表初始化(聚合初始化)(而不是构造然后返回)

如果您改用 return B(),编译器会拒绝此代码。

由于B 不再是聚合类型(C++20 表示具有 any em> 用户声明的构造函数不是聚合)。

【讨论】:

【参考方案2】:

C++ 标准中哪里提到在这种情况下不需要复制/移动构造函数?

不是。没有必要提及这样的事情,因为没有指定 copy-list-initialization 进行任何复制或移动。

return ...;is defined as follows的行为:

带有花括号初始化列表的返回语句通过复制列表初始化初始化要从函数返回的对象或引用

所以这个语法调用了copy-list-initialization,braced-init-list是初始化器,函数的返回值对象是要初始化的对象。所以它在功能上等同于T return_value_object = ...;

[dcl.init.list]/3 explains列表初始化的整个过程,并没有说要初始化的对象是从某个T复制或移动的(其中T是正在初始化的对象类型)。不会创建 T 类型的临时对象或任何类似的对象。大括号初始化列表只是初始化对象。

因此,T 上不需要复制或移动构造函数 除非括号内初始化列表的成员本身需要一个(如果您提供了 T 类型的对象例如,作为列表的成员)。

请注意,这不是省略。省略意味着复制/移动会发生但已被优化。列表初始化没有任何复制或移动以优化开始。

【讨论】:

初始化要从函数返回的对象可以在初始化对象然后“返回”它时读取:/ @LanguageLawyer:不,不能这样读。 “要返回的对象”描述了正在初始化的对象。这里没有歧义。 如果对象已经在调用方,则不需要“返回”。 @LanguageLawyer:在这方面,我不知道您所说的“调用方”是什么意思。返回值对象在两边。由函数的return语句初始化,在调用处通过prvalue访问。

以上是关于使用复制列表初始化从函数返回,不需要复制/移动构造函数 - C++ 11 标准中在哪里说明?的主要内容,如果未能解决你的问题,请参考以下文章

从函数返回对象时调用C ++中的复制构造函数?

笔记十:复制构造函数深拷贝浅拷贝

显式复制构造函数和统一初始化

如何从派生类复制构造函数调用基类复制构造函数? [复制]

可移动构造 可复制构造 可移动赋值 可复制赋值

复制构造函数2——深入理解