假设我有以下代码:
int main()
{
std::vector<std::string> strs;
std::string var("Hello World");
// Make some modifications to 'var'
strs.push_back(std::move(var));
}
我想指出的样本部分是std::move()
的用法。基本上我担心push_back()
电话上的副本。假设我添加的字符串非常大。我还在学习C ++ 11 r值引用,所以我不确定编译器如何在没有std::move()
的情况下优化副本(如果有的话)。
有人可以解释一下这是不成熟的优化(一般情况下,在所有你想要避免副本的情况下强制移动)?如果是这样,我应该期望编译器遵循哪些模式(或者最有可能遵循),这将导致优化和自动移动?
我想补充一点,我理解自动移动如何在函数返回值上发生,因为NRVO / RVO适用。我在这里给出的具体例子不适用于RVO,所以我不确定。
答案 0 :(得分:28)
我不确定编译器如何在没有
std::move()
的情况下优化副本(如果有的话)。
只有非常聪明的编译器可以优化它,所以如果副本可能很昂贵(例如一个非常长的字符串),那么最好移动它。
如果没有移动,代码实际上是一系列调用:
strlen // count "Hello World"
malloc // allocate memory for string var
strcpy // copy data into var
malloc // re-allocate vector
free // deallocate old vector
malloc // allocate new string
strcpy // copy from var to new string
free // destroy var
随着移动它变成:
strlen // count "Hello World"
malloc // allocate memory for string var
strcpy // copy data into var
malloc // re-allocate vector
free // deallocate old vector
理论上,智能编译器可以自动执行该转换,但是编译器可以查看构造函数和析构函数引入的所有抽象层以及vector
成员函数是非常困难的,因此证明代码可以转换为删除malloc
,free
很复杂。
答案 1 :(得分:23)
其他答案过于关注我的口味问题的技术方面,所以我会尝试给出更一般的答案。
简而言之:不,使用像std :: move这样的“技巧”就像在问题中描述的那样,并不是一个过早的优化。在可以使用std::move
时不使用它也没关系,除非已经知道代码对性能至关重要。
避免过早优化的需要有时被理解为“不要优化,直到你能证明它是必要的”,但我更喜欢把它读作:“不要浪费时间解决问题,除非你知道他们需要解决”。
过早优化需要花费精力才能优化可能不需要优化的内容,并且通常会在执行此操作时将简单问题转换为复杂问题。从这个角度来看,我会将对问题本身的任何长期思考归类为过早优化。
相关示例:从事性能关键代码的人员通常会将参数作为const引用传递(const std::string&
)。因为这是他们习惯做的事情,他们会在代码中使用相同的模式,这些模式不是性能关键,即使他们可以只使用pass-by-copy(const std::string
,甚至std::string
) 。这也不是一个不成熟的优化。
答案 2 :(得分:20)
在std::move
之后,原始对象(在本例中为var
)必须处于有效状态,但可以保留任何值,例如它可能是空的。
如果你知道你不打算使用var
并且你只创建它以放入vector
那么它并不是真正的“过早”优化,因为你想要的是什么意思做。
vector
有一个新方法emplace_back()
,它是相同但更清晰的,并使用前向参数(如果您正在构建它,只需执行emplace_back("Hello World")
)。在您的情况下,因为您“使用var进行一些修改”emplace_back
不太可能不合适。
在旧C++
中,您可以通过对空字符串执行push_back()
然后交换它来优化副本。
答案 3 :(得分:18)
我不知道为什么每个人都建议您使用emplace_back()
。 emplace_back()
的目的是通过在适当的位置构建对象来避免复制/移动操作。在这种情况下,您已经构造了对象,因此至少1次复制/移动是不可避免的。在这种情况下,使用emplace_back()
优于push_back()
没有任何优势。
否则,我同意其他所有人说这不是一个不成熟的优化,因为移动语义模拟了你想要做的事情而不是制作一个对象的副本。
答案 4 :(得分:1)
是的,如果是过早优化,那就是过早优化。
让我详细说明:
过早优化的定义是您是否正在优化您不知道对性能至关重要的部分代码。也就是说,过早优化永远不会被优化方法定义,它总是由优化的位置和你对你正在做的事情的知识来定义。
现在,使用std::move()
进行优化是可取的:
使用std::move()
可以避免繁重的复制构造,例如内存分配,释放,复制构造包含的元素,解构包含的元素等等。这很好。但有一部分要记住:容器对象的构造/破坏。
emplace_back()
具有完全避免构建临时对象的优点。因此,只要您可以使用emplace_back()
来避开临时对象,就可以赢得一小笔胜利。
考虑代码质量(读取可读性/可维护性):std::move()
有一个缺点,就是给你留下一个不可用的对象,这可能是bug的来源。 emplace_back()
不会发生这种情况。所以,再次,后者显然更可取。
当然,emplace_back()
不能在允许使用std::move()
的所有上下文中使用。因此,如果这些对于性能至关重要,那么std::move()
可能是最佳选择。使用std::move()
进行优化有一些非常有效的用例。但是,您可能还会发现,您可以以不需要任何复制/移动/安装构造的方式编写代码。在优化时永远不要停止寻找更好的解决方案!
最后,使用std::move()
进行优化的有效性仍然完全取决于上下文:无论何时进行优化,这都是很好的优化。