【发布时间】:2013-11-08 23:03:27
【问题描述】:
假设我有字符串向量,我想通过 std::accumulate 连接它们。
如果我使用以下代码:
std::vector<std::string> foo{"foo","bar"};
string res="";
res=std::accumulate(foo.begin(),foo.end(),res,
[](string &rs,string &arg){ return rs+arg; });
我可以肯定会有临时对象构造。
在this 的回答中,他们说 std::accumulate 的效果是这样指定的:
通过初始化累加器 acc 来计算结果 初始值 init 然后用 acc = acc + *i 或 acc = 修改它 binary_op(acc, *i) 对于 [first,last) 范围内的每个迭代器 i 顺序。
所以我想知道避免不必要的临时对象构造的正确方法是什么。
一个想法是用这种方式改变 lambda:
[](string &rs,string &arg){ rs+=arg; return rs; }
在这种情况下,我认为我强制有效地连接字符串并帮助编译器(我知道我shouldn't)省略了不必要的副本,因为这应该等同于(伪代码):
accum = [](& accum,& arg){ ...; return accum; }
因此
accum = & accum;
另一个想法是使用
accum = [](& accum,& arg){ ...; return std::move(accum); }
但这可能会导致类似:
accum = std::move(& accum);
这在我看来非常可疑。
编写此代码的正确方法是什么以最大程度地减少不必要地创建临时对象的风险?我不仅对 std::string 感兴趣,我很高兴有一个解决方案,它可能适用于任何实现了复制和移动构造函数/赋值的对象。
【问题讨论】:
-
你应该只为连接创建一个函数...
-
一个丑陋的替代方法是使用指向本地
std::string变量的指针作为累加器,并且可能事先使用reserve。虽然现在,accumulate仅简化为for_each,并不比下面大卫的解决方案好多少。 -
看来
std::accumulate会总是制作临时副本。如果这是不可接受的,那么您需要使用其他东西。 -
@MarkRansom
std::accumulate不会制作临时副本;它调用的operator+会生成额外的副本。 (operator=也可能最终被复制;使用 C++ 和移动语义,它可能不会,但在早期版本中它会。) -
C++20 将其指定为
acc = move(acc) + rhs,这可以大大减少复制成本不高的类型的累积。例如,一个好的std::string实现将有一个operator+(string&& lhs, T)劫持lhs,附加到它,然后返回它(这是RVOable)。 @R.MartinhoFernandes 一个不那么丑陋的等价物是在reference_wrapper中积累,就像在this answer 中一样,但是是的,我确实想知道这是否真的比for[_each]具有捕获的参考更好/更好。我猜可能有点,语义上?
标签: c++ algorithm c++11 vector accumulate