它在[C++ 11:12.8/31]中说明:
复制/移动操作的省略,称为复制省略,允许[...]:
- 在具有类返回类型的函数的return语句中,当表达式是具有与函数返回类型相同的cv-unqualified类型的非易失性自动对象(除函数或catch子句参数之外)的名称时,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作
这意味着
#include <iostream>
using namespace std;
struct X
{
X() { }
X(const X& other) { cout << "X(const X& other)" << endl; }
};
X no_rvo(X x) {
cout << "no_rvo" << endl;
return x;
}
int main() {
X x_orig;
X x_copy = no_rvo(x_orig);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
将打印
X(const X& other)
no_rvo
X(const X& other)
Run Code Online (Sandbox Code Playgroud)
为什么需要第二个拷贝构造函数?编译器不能简单地延长x的生命周期吗?
对于任何此语法,类必须具有有效的副本或移动构造函数才是合法的:
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时,复制初始化及其替代的直接初始化始终遵循列表初始化.添加时没有语义效果=,这是列表初始化非正式地称为统一初始化的一个原因.
存在差异:与复制初始化不同,直接初始化可以调用显式构造函数.复制初始化初始化临时并复制它以在转换时初始化对象.
的规范 …
我无法弄清楚为什么在最后一种情况下启用了复制省略时调用的移动构造函数(甚至是强制性的,例如在C++ 17中):
class X {
public:
X(int i) { std::clog << "converting\n"; }
X(const X &) { std::clog << "copy\n"; }
X(X &&) { std::clog << "move\n"; }
};
template <typename T>
X make_X(T&& arg) {
return X(std::forward<T>(arg));
}
int main() {
auto x1 = make_X(1); // 1x converting ctor invoked
auto x2 = X(X(1)); // 1x converting ctor invoked
auto x3 = make_X(X(1)); // 1x converting and 1x move ctor invoked
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,哪些规则阻碍了移动构造函数被省略?
UPDATE
调用移动构造函数时可能更直接的情况: …
c++ language-lawyer move-semantics copy-elision perfect-forwarding
我有一个无法上课的课程.复制这将是有问题的.我想保证它不会被复制,所以我制作了它的复制构造函数deleted:
class A {
public:
A();
A(const A&) = delete;
};
A fun() {
return A();
};
int main() {
A a = fun();
};
Run Code Online (Sandbox Code Playgroud)
不幸的是,g ++不能编译这个原因:
t.cc: In function ‘A fun()’:
t.cc:8:12: error: use of deleted function ‘A::A(const A&)’
return A();
^
t.cc:4:5: note: declared here
A(const A&) = delete;
^
t.cc: In function ‘int main()’:
t.cc:12:13: error: use of deleted function ‘A::A(const A&)’
A a = fun();
^
t.cc:4:5: note: declared here
A(const A&) …Run Code Online (Sandbox Code Playgroud) 是否有任何警告,让我们知道在海湾合作委员会中是否进行了NRVO/RVO?
我发现-fno-elide-constructors关闭NRVO/RVO,但NRVO/RVO有自己的条件发生,有时不会发生.当需要额外的复制构造时,需要知道是否发生了NRVO/RVO.
我对编译时功能特别感兴趣.如果有一些特定的#pragma GCC...(它会立即激活诊断)或使用静态断言机制的东西会很好.
假设我有以下类型:
struct X {
X& operator+=(X const&);
friend X operator+(X lhs, X const& rhs) {
lhs += rhs;
return lhs;
}
};
Run Code Online (Sandbox Code Playgroud)
我有声明(假设所有命名变量都是类型的左值X):
X sum = a + b + c + d;
Run Code Online (Sandbox Code Playgroud)
在C++ 17中,我对这个表达式执行的副本和移动有多少保证?那么无保障的省略呢?
我看到很多代码在工作中人们使用emplace和emplace_back与临时对象,如下所示:
struct A {
A::A(int, int);
};
vector<A> v;
vector<A>.emplace_back(A(1, 2));
Run Code Online (Sandbox Code Playgroud)
我知道emplace_back的重点是能够直接传递参数,如下所示:
v.emplace_back(1, 2);
Run Code Online (Sandbox Code Playgroud)
但不幸的是,一些人并不清楚这一点.但是,我们不要纠缠于此......
我的问题是:编译器是否能够对此进行优化并跳过创建和复制?或者我应该真的尝试修复这些事件?
供您参考......我们正在使用C++ 14.
特定
struct Range{
Range(double from, double to) : from(from), to(to) {}
double from;
double to;
};
struct Box{
Box(Range x, Range y) : x(x), y(y) {}
Range x;
Range y;
};
Run Code Online (Sandbox Code Playgroud)
假设我们跑了Box box(Range(0.0,1.0),Range(0.0,2.0)).
启用优化的现代编译器是否可以避免Range在此构造期间完全复制对象?(即构建Range内部的对象box以开始?)
考虑这段代码,它定义了一个简单的结构Test(带有默认构造函数和复制构造函数)并std::pair <Test, Test>从函数返回 a 。
#include <iostream>
#include <utility>
using namespace std;
struct Test {
Test() {}
Test(const Test &other) {cout << "Test copy constructor called\n";}
};
auto func() {
return make_pair(Test(), Test());
}
int main()
{
auto [a, b] = func(); // Expectation: copies for a and b are both elided
return 0;
}
Run Code Online (Sandbox Code Playgroud)
令人惊讶的是,输出是
Test copy constructor called
Test copy constructor called
Run Code Online (Sandbox Code Playgroud)
而修改func为
Test copy constructor called
Test copy constructor called
Run Code Online (Sandbox Code Playgroud)
导致复制构造函数未被调用。我的 …
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html
上述"保证副本Elision"提案在2016年6月在芬兰奥卢举行的会议上被投票选入C++工作文件,然后被投票作为委员会草案出版.希望明年能够成为C++ 17标准的出版物.
该提案阐明了涉及临时对象的各种值类别,以在某些用例中强制执行复制构造函数调用.
我的问题是"这些新要求可能会破坏编译器的ABI兼容性,这些编译器可能以前没有在这些情况下完成复制,或者是以不符合新要求的方式实现的吗?"
我正在考虑初始化之类的东西,当创建对象时可以内联,而不是在跨越编译单元边界时.