c++03 libstdc++ 与 c++11 中的虚假副本
Spurious copies in c++03 libstdc++ vs c++11
考虑这段代码:
#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;
}
在 gcc 中使用 -std=c++11 编译,你得到输出,
Param
Default
Assignment
删除-std=c++11,然后你得到,
Param
Default
Copy
Copy
Assignment
libc++ example producing the superior output in c++03 mode
多出来的两份是哪里来的?
它们与调用下标运算符有关,与赋值无关。 (如果您删除分配,它们仍然存在。)对我来说,它们似乎并不需要,即使在 C++11 之前的世界中,如 libc++ 示例所示。
最初的动机是看 this question
这是LWG 334:
C++03 标准为 operator[]
([lib.map.access]p1) 规定了以下效果:
Returns: (*((insert(make_pair(x, T()))).first)).second
.
libstdc++在C++03模式下实现operator[]
使用的插入(在key不存在的情况下)如下:
__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, value_type(__k, _Tp()));
提交消息说这是为了
Update to SGI STL 3.11.
Earlier versions of the SGI STL (1995) 确实以与 C++03 标准相同的方式指定了 map::operator[]
:
For a map m
and key k
, 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(..
到 libstdc++ 中仍然存在的形式,使用 lower_bound
并且只创建如果密钥尚不存在,则配对。
所以可以说 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, inserts value_type(x, T())
into the map.
(其中x
是operator[]
的参数)
考虑这段代码:
#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;
}
在 gcc 中使用 -std=c++11 编译,你得到输出,
Param
Default
Assignment
删除-std=c++11,然后你得到,
Param
Default
Copy
Copy
Assignment
libc++ example producing the superior output in c++03 mode
多出来的两份是哪里来的?
它们与调用下标运算符有关,与赋值无关。 (如果您删除分配,它们仍然存在。)对我来说,它们似乎并不需要,即使在 C++11 之前的世界中,如 libc++ 示例所示。
最初的动机是看 this question
这是LWG 334:
C++03 标准为 operator[]
([lib.map.access]p1) 规定了以下效果:
Returns:
(*((insert(make_pair(x, T()))).first)).second
.
libstdc++在C++03模式下实现operator[]
使用的插入(在key不存在的情况下)如下:
__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, value_type(__k, _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(..
到 libstdc++ 中仍然存在的形式,使用 lower_bound
并且只创建如果密钥尚不存在,则配对。
所以可以说 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[]
的参数)