c ++ 03 libstdc ++与c ++ 11中的虚假副本

tah*_*ith 6 c++ libstdc++ 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;
}
Run Code Online (Sandbox Code Playgroud)

使用-std = c ++ 11在gcc中编译并获得输出,

Param
Default
Assignment
Run Code Online (Sandbox Code Playgroud)

删除-std = c ++ 11,然后你得到,

Param
Default
Copy
Copy
Assignment
Run Code Online (Sandbox Code Playgroud)

用c ++ 11

libc ++示例在c ++ 03模式下生成高级输出

这两份额外的副本来自哪里?

它们与调用下标运算符有关,而与调用无关.(如果删除了作业,它们仍然存在.)对于我来说,即使在C++之前的11世界中,它们也似乎并不需要,正如libc ++示例所示.

这最初的动机是看这个问题

dyp*_*dyp 9

这是LWG 334:

C++ 03 Standard要求operator[]([lib.map.access] p1)具有以下效果:

返回: (*((insert(make_pair(x, T()))).first)).second.


libstdc ++实现了operator[]在C++ 03模式下使用的插入(在密钥不存在的情况下),如下所示:

 __i = insert(__i, value_type(__k, mapped_type()));
Run Code Online (Sandbox Code Playgroud)

__i 是插入点,它被计算为

iterator __i = lower_bound(__k);
Run Code Online (Sandbox Code Playgroud)

__k是参数operator[].

临时的创建value_type(__k, mapped_type())导致第一副本(从mapped_type()进入value_type对).第二个副本是结果insert,它将该value_type对复制到实际节点中.

1997年的原始版本是:

return (*((insert(value_type(k, T()))).first)).second;
Run Code Online (Sandbox Code Playgroud)

这几乎是标准的字母(当时甚至不存在!).它最后一次改变是在1998年.在此之前,它使用了:

__i = insert(__i, value_type(__k, _Tp()));
Run Code Online (Sandbox Code Playgroud)

提交消息说这是

更新到SGI STL 3.11.


早期版本的SGI STL(1995)确实map::operator[]以与C++ 03标准相同的方式指定:

对于地图m和键k,m[k]在语义上等同于(*((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的建议解决方案:

效果:如果x地图中没有等效的键,则插入value_type(x, T())到地图中.

(x参数在哪里operator[])