Pot*_*ter 21 c++ initialization copy-elision c++11
对于任何此语法,类必须具有有效的副本或移动构造函数才是合法的:
C x = factory();
C y( factory() );
C z{ factory() };
Run Code Online (Sandbox Code Playgroud)
在C++ 03中,依赖复制省略来阻止编译器触及复制构造函数是相当普遍的.无论定义是否存在,每个类都有一个有效的复制构造函数签名.
在C++ 11中,应该定义一个不可复制的类型C( C const & ) = delete;,无论使用什么,对函数的任何引用都是无效的(对于不可移动的相同).(C++11§8.4.3/ 2).例如,GCC会在尝试按值返回此类对象时抱怨.复制省立不再有帮助.
幸运的是,我们还有新的语法来表达意图而不是依赖于漏洞.该factory函数可以返回一个braced-init-list来临时构造结果:
C factory() {
return { arg1, 2, "arg3" }; // calls C::C( whatever ), no copy
}
Run Code Online (Sandbox Code Playgroud)
编辑:如果有任何疑问,此return语句解析如下:
不要被名称copy-list-initialization误导.8.5:
13:初始化的形式(使用括号或
=)通常是无关紧要的,但是当初始化器或正在初始化的实体具有类类型时,它很重要; 见下文.如果正在初始化的实体没有类类型,则带括号的初始值设定项中的表达式列表应为单个表达式.14:在表单
T x = a;中以及在参数传递,函数返回,抛出异常(15.1),处理异常(15.3)和聚合成员初始化(8.5.1)时发生的初始化称为复制初始化.
当初始化器是braced-init-list时,复制初始化及其替代的直接初始化始终遵循列表初始化.添加时没有语义效果=,这是列表初始化非正式地称为统一初始化的一个原因.
存在差异:与复制初始化不同,直接初始化可以调用显式构造函数.复制初始化初始化临时并复制它以在转换时初始化对象.
的规范副本列表初始化的return { list }声明仅指定了完全等效的语法是temp T = { list };,在这里=表示拷贝初始化.它不会立即暗示调用复制构造函数.
- 结束编辑.
然后可以将函数结果接收到右值引用中,以防止将临时值复制到本地:
C && x = factory(); // applies to other initialization syntax
Run Code Online (Sandbox Code Playgroud)
问题是,如何从返回不可复制,不可移动类型的工厂函数初始化非静态成员?引用技巧不起作用,因为引用成员不会延长临时的生命周期.
注意,我不考虑聚合初始化.这是关于定义构造函数.
关于你的主要问题:
问题是,如何从返回不可复制、不可移动类型的工厂函数初始化非静态成员?
你不知道。
您的问题是您试图混淆两件事:如何生成返回值以及如何在调用站点使用返回值。这两件事彼此没有联系。请记住:函数的定义不会影响它的使用方式(就语言而言),因为该定义不一定可供编译器使用。因此,C++ 不允许返回值的生成方式影响任何内容(除了省略之外,这是一种优化,而不是语言要求)。
换句话说,这个:
C c = {...};
Run Code Online (Sandbox Code Playgroud)
与此不同的是:
C c = [&]() -> C {return {...};}()
Run Code Online (Sandbox Code Playgroud)
您有一个按值返回类型的函数。它返回类型为 的纯右值表达式C。如果你想存储这个值,从而给它一个名字,你有两个选择:
将其存储为const&或&&。这会将临时块的生命周期延长到控制块的生命周期。你不能用成员变量来做到这一点;它只能通过函数中的自动变量来完成。
将其复制/移动到一个值中。您可以使用成员变量来完成此操作,但显然要求类型可复制或可移动。
如果您想要存储纯右值表达式,这些是 C++ 提供的唯一选项。因此,您可以使类型可移动,或者返回一个新分配的内存指针并存储它而不是值。
这种限制是创建移动的一个重要原因:能够按值传递事物并避免昂贵的副本。无法更改语言以强制省略返回值。因此,他们在很多情况下降低了成本。