[英]Insert elements into std::map without extra copying
考虑以下程序:
#include <map>
#include <string>
#define log magic_log_function // Please don't mind this.
//
// ADVENTURES OF PROGO THE C++ PROGRAM
//
class element;
typedef std::map<int, element> map_t;
class element {
public:
element(const std::string&);
element(const element&);
~element();
std::string name;
};
element::element(const std::string& arg)
: name(arg)
{
log("element ", arg, " constucted, ", this);
}
element::element(const element& other)
: name(other.name)
{
name += "-copy";
log("element ", name, " copied, ", this);
}
element::~element()
{
log("element ", name, " destructed, ", this);
}
int main(int argc, char **argv)
{
map_t map1; element b1("b1");
log(" > Done construction.");
log(" > Making map 1.");
map1.insert(std::pair<int, element>(1, b1));
log(" > Done making map 1.");
log(" > Before returning from main()");
}
它在堆栈上创建一些对象, insert
它们插入std::map
容器中,在此过程中创建两个额外的临时副本:
element b1 constucted, 0x7fff228c6c60
> Done construction.
> Making map 1.
element b1-copy copied, 0x7fff228c6ca8
element b1-copy-copy copied, 0x7fff228c6c98
element b1-copy-copy-copy copied, 0x232d0c8
element b1-copy-copy destructed, 0x7fff228c6c98
element b1-copy destructed, 0x7fff228c6ca8
> Done making map 1.
> Before returning from main()
element b1 destructed, 0x7fff228c6c60
element b1-copy-copy-copy destructed, 0x232d0c8
我们可以通过将std::pair
签名更改为std::pair<int, element&>
来摆脱一个额外的副本构造函数调用,但是,第二个临时变量仍然被创建并被立即销毁:
element b1 constucted, 0x7fff0fe75390
> Done construction.
> Making map 1.
element b1-copy copied, 0x7fff0fe753c8
element b1-copy-copy copied, 0x1bc4098
element b1-copy destructed, 0x7fff0fe753c8
> Done making map 1.
> Before returning from main()
element b1 destructed, 0x7fff0fe75390
element b1-copy-copy destructed, 0x1bc4098
有没有一种方法可以使std::map
通过引用仅将对象放在堆栈上并对其进行单个内部复制?
这是激发C++11
的move
功能的众多用例之一,并受到许多新功能(特别是右值引用)和各种新的标准库接口(包括std::map::emplace
, std::vector::emplace_back
等。
如果由于某种原因您仍然不能使用C++11
,那么您至少可以以解决问题为己任,认为解决方案已经标准化并已实施并且认为我们中许多人正在使用它的控制台,我们中的某些人[1]在生产代码中。 and it's your call as to when you take it up. 因此,正如老玩笑所说的那样, ,这是您何时接受它的电话。
请注意,如果您的对象实现了移动构造函数,则不必使用emplace
成员函数,默认情况下,它们甚至可以这样做。 如果具有显式的复制构造函数,则不会发生这种情况,因此,上面的测试可能会产生观察者效果(实际上,在POD的情况下,它也可能会抑制编译器的优化,因此即使使用C ++ 03,您也可能不会遇到问题。想你做的)。
有许多可用的技巧,可以避免仅使用“较小”源代码更改的副本,但是恕我直言,最好的方法是开始向C ++ 11迈进。 无论您做什么,都请尝试以一定的方式来减少不可避免的迁移带来的痛苦。
[注1]:免责声明:我不再写生产代码,已经退休了,所以我不属于那句话中的“我们中的一些人”。
我去过的标准实践(使用较旧的C ++版本)是使用共享指针的Map。
仍然会创建共享指针的副本,但是通常比复制大型对象要麻烦得多。
您可以使用emplace() :
元素是就地构造的,即不执行复制或移动操作。 使用与提供给函数的参数完全相同的参数调用元素类型(value_type,即std :: pair)的构造函数
好吧,如果您没有emplace
,则可以在堆上构造元素并传递指向map的指针:
typedef std::map<int, element*> map_t;
...
printf(" > Making pair 1.\n");
std::pair<int, element*> pair(1, new element ("b1")) ;
printf(" > Making map 1.\n");
map1.insert(pair);
但是如果您不在地图离开范围的范围内,那么就会受到内存泄漏的困扰...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.