#include <iostream> #include <string> #include <map> using namespace std; class Foo { public: Foo() : _x(0) { cout << "Default" << endl; } Foo(int a) : _x(a) { cout << "Param" << endl; } Foo(Foo const &foo) : _x(foo._x) { cout << "Copy" << endl; } Foo& operator=(Foo const &foo) { cout << "Assignment" << endl; _x = foo._x; return *this; } int get(void) { return _x; } private: int _x; }; int main(int argc,char *argv []) { std::map<int,Foo> foos; Foo a_foo(10); foos[100] = a_foo; return 0; }
使用-std = c 11在gcc中编译并获得输出,
Param Default Assignment
删除-std = c 11,然后你得到,
Param Default Copy Copy Assignment
libc++ example producing the superior output in c++03 mode
这两份额外的副本来自哪里?
它们与调用下标运算符有关,而与调用无关. (如果你删除了作业,它们仍然存在.)对我来说,他们似乎并不需要,即使在前C11世界中,正如libc示例所示.
这最初的动机是看this question
解决方法
C 03标准要求operator []([lib.map.access] p1)具有以下效果:
Returns:
(*((insert(make_pair(x,T()))).first)).second
.
libstdc在C03模式下实现operator []使用的插入(在密钥不存在的情况下),如下所示:
__i = insert(__i,value_type(__k,mapped_type()));
__i是插入点,计算方式为
iterator __i = lower_bound(__k);
__k是operator []的参数.
临时value_type(__ k,mapped_type())的创建导致第一个副本(从mapped_type()到value_type对).第二个副本是insert的结果,它将value_type对复制到实际节点中.
1997年的原始版本是:
return (*((insert(value_type(k,T()))).first)).second;
这几乎是标准的字母(当时甚至不存在!).它最后一次改变是在1998年.在此之前,它使用了:
__i = insert(__i,_Tp()));
提交消息说这是
Update to SGI STL 3.11.
Earlier versions of the SGI STL (1995)确实以与C 03标准相同的方式指定了map :: operator []:
For a map
m
and keyk
,m[k]
is semantically equivalent to(*((m.insert(make_pair(k,T()))).first)).second
.
SGI STL v2.03(1997)已经切换到使用value_type而不是make_pair.正如gcc的提交日志所暗示的那样,SGI STL的实现在v3.0(也是1997)和v3.11(1998)之间再次从insert(value_type(..到使用lower_bound的libstdc中仍存在的形式)再次改变,如果密钥尚不存在.
所以可以说libstdc实现了LWG 334的第一个提议解决方案(value_type而不是make_pair).然而,这并不是发生的事情,看着它的历史.它只是遵循SGI STL.在这方面,libc并不严格遵守C 03.
libstdc的同一运算符的C 11版本使用自定义安置功能. C 11标准的map :: operator []规范遵循LWG 334的建议解决方案:
Effects: If there is no key equivalent to
x
in the map,insertsvalue_type(x,T())
into the map.
(其中x是operator []的参数)