使用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
,但它消除了分配。使第二种行为成为可能和可用的便捷方法是什么?它是否计划在未来的C ++版本中使用?
我的猜测是,除了写作之外,C ++仍然没有提供:
memcpy
我是对的吗?
答案 0 :(得分:2)
您实际上可以通过定义operator =来获取初始化列表。 对于std :: list,只需调用
X = {"foo","bar","dead","beef"}.
在您的情况下,实际发生的事情是:
对于大多数对象,例如std :: list,与简单构造对象相比,这实际上并不昂贵。
但是,它仍会为第二个std :: list的内部存储带来额外的分配,这可以避免:如果可能的话,我们可以重用已经为X分配的内部存储。发生的事情是:
某些对象重载赋值运算符以获取初始值设定项列表,std::vector和std::list就是这种情况。这样的运营商可以使用已经在内部分配的存储,这是最有效的解决方案。
//请在此处插入关于过早优化的常规说法
答案 1 :(得分:1)
是否计划在未来的C ++版本中使用?
没有。谢天谢地。
分配不与destroy-then-create相同。您的任务示例中未销毁X
。 X
是一个活动对象; X
的内容可能会被销毁,但X
本身永远不会被销毁。并且也不应该。
如果你想要销毁X
,那么你就可以使用explicit-destructor-and-placement-new。虽然由于const
成员的可能性,如果您想要安全,您还需要清洗指向该对象的指针。但是,永远不会被视为等同于此。
如果您关心效率,那么使用assign
成员函数要好得多。通过使用assign
,X
有机会重用现有分配。而这几乎肯定会使它比你的&#34; destroy-plus-construct&#34;更快。版。将链表移动到另一个对象的成本是微不足道的;不得不销毁所有这些分配而仅重新分配它们的成本不是。
这对于std::list
尤其重要,因为它有批次的分配。
最糟糕的情况是,assign
的效率不会低于你在课堂外提出的任何其他方面。最好的情况是,它会好得多。
答案 2 :(得分:1)
当您有涉及移动分配的声明时:
x = std::move(y);
在移动之前,不会为x
调用析构函数。但是,在移动之后,在某些时候将为y
调用析构函数。移动赋值运算符背后的想法是它可以以一种简单的方式将y
的内容移动到x
(例如,将指针复制到y
的存储器x
)。它还必须确保其先前的内容被正确销毁(它可能选择与y
交换它,因为您知道y
可能不再使用,而y
的析构函数将会被称为。
如果内联移动分配,编译器可能会推断出将存储从y
移动到x
所需的所有操作都等同于就地构造。
答案 3 :(得分:1)
重新提出你的最后一个问题
“我是对的吗?
没有
您对什么是允许或不允许的想法是错误的。只要保留了可观察的效果,就允许编译器替换任何优化。这被称为&#34;好像&#34;规则。可能的优化包括删除所有代码,如果它不影响任何可观察的代码。特别是,您的&#34;不允许&#34;对于第二个例子是完全错误的,并且推理&#34;它消除了分配&#34;也适用于你的第一个例子,你得出相反的结论,即那里存在自相矛盾。