问题描述
|
我仍然是新手程序员,我知道过早的优化是不好的,但是我也知道复制大量内容也是不好的。
我已经阅读了复制要素及其同义词,但例如在Wikipedia上的示例使我似乎只有在要返回的对象在完全构造的同时返回时,复制要素才能发生。
向量之类的对象呢?通常仅在填充某些东西并用作返回值时才有意义。
毕竟,空向量只能手动实例化。
那么,它在这种情况下是否也起作用?
简洁的坏风格:
vector<foo> bar(string baz)
{
vector<foo> out;
for (each letter in baz)
out.push_back(soMetable[letter]);
return out;
}
int main()
{
vector<foo> oof = bar(\"Hello World\");
}
使用bar(vector&out,字符串文本)我没有真正的麻烦,但是上面的方法看起来更好,从美学上来说也有意图。
解决方法
以维基百科上的示例为例,在我看来,仅当要返回的对象在完全构造好同时返回时才可以进行复制省略。
那是误导(读:错误)。问题在于,所有代码路径中仅返回一个对象,即潜在返回对象的仅一种构造正在发生。
您的代码很好,任何现代编译器都可以删除该副本。
另一方面,以下代码可能会产生问题:
vector<int> foo() {
vector<int> a;
vector<int> b;
// … fill both.
bool c;
std::cin >> c;
if (c) return a; else return b;
}
在这里,编译器需要完全构造两个不同的对象,并且只有稍后再决定要返回哪个对象,因此编译器必须复制一次,因为它无法直接在目标内存位置构造返回的对象。
,没有什么可以阻止编译器删除副本。在12.8.15中定义:
[...]复制操作的省略是
在以下允许
情况(可能会合并
消除多个副本):
[...]
当一个临时类对象具有
未绑定到参考(12.2)
将被复制到一个类对象
相同的简历不合格类型,副本
操作可以通过以下方式省略
构造临时对象
直接进入目标
省略的副本
是否确实取决于编译器和您使用的设置。
,vector
的两个隐含副本都可以并且经常被删除。命名返回值优化可以消除返回语句return out;
中隐含的副本,并且也可以消除eliminated4ѭ副本初始化中所隐含的。
在同时进行两种优化的情况下,vector<foo> out;
中构造的对象与oof
中的对象相同。
使用诸如此类的人工测试用例,可以更轻松地测试正在执行哪些优化。
struct CopyMe
{
CopyMe();
CopyMe(const CopyMe& x);
CopyMe& operator=(const CopyMe& x);
char data[1024]; // give it some bulk
};
void Mutate(CopyMe&);
CopyMe fn()
{
CopyMe x;
Mutate(x);
return x;
}
int main()
{
CopyMe y = fn();
return 0;
}
复制构造函数已声明但未定义,因此无法内联和消除对其的调用。使用现在相对较旧的gcc 4.4进行编译,结果为-O3 -fno-inline
(过滤为C ++名称分解并进行编辑以删除非代码)。
fn():
pushq %rbx
movq %rdi,%rbx
call CopyMe::CopyMe()
movq %rbx,%rdi
call Mutate(CopyMe&)
movq %rbx,%rax
popq %rbx
ret
main:
subq $1032,%rsp
movq %rsp,%rdi
call fn()
xorl %eax,%eax
addq $1032,%rsp
ret
可以看出,没有对复制构造函数的调用。实际上,即使在-O0
,gcc也会执行这些优化。您必须提供ѭ11才能关闭此行为;如果执行此操作,则gcc将对CopyMe
的副本构造函数生成两次调用-在对fn()
的调用内部进行一次,在外部进行一次调用。
fn():
movq %rbx,-16(%rsp)
movq %rbp,-8(%rsp)
subq $1048,%rsp
movq %rdi,%rbx
movq %rsp,%rdi
call CopyMe::CopyMe()
movq %rsp,%rdi
call Mutate(CopyMe&)
movq %rsp,%rsi
movq %rbx,%rdi
call CopyMe::CopyMe(CopyMe const&)
movq %rbx,%rax
movq 1040(%rsp),%rbp
movq 1032(%rsp),%rbx
addq $1048,%rsp
ret
main:
pushq %rbx
subq $2048,%rdi
call fn()
leaq 1024(%rsp),%rdi
movq %rsp,%rsi
call CopyMe::CopyMe(CopyMe const&)
xorl %eax,%eax
addq $2048,%rsp
popq %rbx
ret