[英]Reducing assignment of temporary object to in-place construction
以支持移动语义的std::list
为例。
std::list<std::string> X;
... //X is used in various ways
X=std::list<std::string>({"foo","bar","dead","beef"});
自C ++ 11以来,编译器执行分配的最直接方法是:
X
std::list
std::list
移至X
现在,不允许编译器执行以下操作:
std::list
因为虽然这显然可以节省另一个memcpy
但却消除了分配。 使第二种行为成为可能和可用的便捷方法是什么? 在将来的C ++版本中计划了吗?
我的猜测是,C ++仍然不提供以下功能:
X.~X();
new(&X) std::list<std::string>({"foo","bar","dead","beef"});
我对吗?
实际上,您可以通过定义operator =来获取初始化程序列表来完成此操作。 对于std :: list,只需调用
X = {"foo","bar","dead","beef"}.
就您而言,实际上是:
在大多数对象上,例如std :: list,与仅构造一个对象相比,这实际上并不昂贵。
但是,它仍然会为第二个std :: list的内部存储引起额外的分配,这可以避免:如果可能的话,我们可以重用已经为X分配的内部存储。 正在发生的是:
一些对象使赋值运算符超载以获取初始化器列表, std :: vector和std :: list就是这种情况。 这样的操作员可以使用已经在内部分配的存储,这是最有效的解决方案。
//请在此处插入有关过早优化的常见问题
在将来的C ++版本中计划了吗?
不用了,谢谢你。
分配是不一样的破坏,然后创建。 X
在您的分配示例中未销毁。 X
是有生命的物体; X
的内容可能会被破坏,但X
本身永远不会被破坏。 而且也不应该 。
如果您想销毁X
,那么您就可以使用显式析构和放置新函数来具有这种能力。 尽管感谢const
成员的可能,但是如果您想确保安全,还需要清洗指向该对象的指针。 但是,分配永远不应该被认为是等效的。
如果您关心效率,那么最好使用assign
成员函数。 通过使用assign
, X
可以重用现有分配。 而且这几乎肯定会使它比您的“ destroy-plus-construct”版本更快。 将链接列表移动到另一个对象中的开销很小。 不必为了重新分配而销毁所有这些分配的成本。
这对于std::list
尤其重要,因为它有很多分配。
在最坏的情况下, assign
效率不会比您从课堂之外提出的其他效率低。 最好的情况是,情况会好得多。
当您有涉及移动分配的语句时:
x = std::move(y);
在执行移动之前,不会为x
调用析构函数。 但是,在移动之后,有时将调用析构函数y
。 移动赋值运算符背后的想法是,它可能能够以一种简单的方式将y
的内容移动到x
(例如,将指向y
的存储的指针复制到x
)。 它还必须确保其先前的内容被正确销毁(它可能选择与y
交换它,因为您知道y
可能不再被使用,并且y
的析构函数将被调用)。
如果内联移动分配,则编译器可能能够推断出将存储从y
移至x
所需的所有操作仅等同于就地构造。
再问你最后一个问题
”“对吗?
没有。
您关于允许或不允许的想法是错误的。 只要编译器保留可观察的效果,就可以替换任何优化。 这称为“好像”规则。 如果不影响任何可见的代码,则可能的优化包括删除所有该代码。 特别是,您对第二个示例的“不允许”完全是错误的,而“它消除分配”的理由也适用于您的第一个示例,在该示例中您得出相反的结论,即那里存在自相矛盾。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.