C++ c+格式的虚假副本+;03 libstdc++;vs c++;11

C++ c+格式的虚假副本+;03 libstdc++;vs c++;11,c++,c++11,libstdc++,C++,C++11,Libstdc++,考虑以下代码: #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"

考虑以下代码:

#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,然后得到

Param
Default
Copy
Copy
Assignment

这两份额外的副本是从哪里来的

它们与调用下标运算符有关,而与赋值无关。(如果删除赋值,它们会保留下来。)对我来说,它们似乎不需要,即使是在C++11之前的世界中,正如libc++示例所示

这最初是通过查看以下内容来实现的:

C++03标准要求
操作符[]
([lib.map.access]p1)具有以下效果:

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


libstdc++在C++03模式下实现
操作符[]
使用的插入(在键还不存在的情况下),如下所示:

 __i = insert(__i, value_type(__k, mapped_type()));
\uu i
是插入点,其计算如下:

iterator __i = lower_bound(__k);
运算符[]
的参数

创建临时
value\u类型(\uk,mapped\u type())
会导致第一次复制(从
mapped\u type()
value\u类型
对)。第二个副本是
insert
的结果,它将
value\u type
对复制到实际节点中

1997年的原始版本为:

return (*((insert(value_type(k, T()))).first)).second;
这几乎符合标准(当时根本不存在!)。上一次发生重大变化是在1998年。在此之前,它使用了:

__i = insert(__i, value_type(__k, _Tp()));
提交消息说这是为了

更新至SGI STL 3.11


确实以与C++03标准相同的方式指定了
map::operator[]

对于map
m
和key
k
m[k]
在语义上等同于
(*((m.insert(make_pair(k,T()))。first))。second

SGI STL v2.03(1997)已经改用
value\u-type
而不是
make\u-pair
。正如gcc的提交日志所显示的那样,SGI STL的实现在v3.0(也是1997年)和v3.11(1998年)之间再次发生了变化,从
insert(value_type(…
)到libstdc++中仍然存在的形式,使用
lower_bound
,并且仅在密钥还不存在时创建对


因此,可以说libstdc++实现了LWG 334的第一个提议的解决方案(
value\u type
,而不是
make\u pair
)。但是,从它的历史来看,这并不是真正发生的事情。它只是在遵循。libc++在这方面并不严格遵守C++03


libstdc++的C++11版本的同一个操作符使用了自定义定位函数。C++11标准的
map::operator[]
规范遵循LWG 334的建议解决方案:

效果:如果地图中没有与
x
等价的键,则将
value\u type(x,T())
插入地图


(其中
x
运算符[]
的参数)

你不能在C++11模式下获得它们,那么为什么不完全删除问题中的所有右值引用函数呢?问题显然不受它们的影响。我认为这是libstdc++内部的QoI问题,我猜需要做一些工作才能无意义地准确确定发生了什么。@lightness racesinorbit它们存在是因为stion不受它们的影响,这与前面的答案相矛盾。通常,当您想知道变量a的效果时,最好不要引入不必要和不相关的变量B。您所指的答案充其量是误导性的。该问题中的
Foo
具有用户提供的复制构造函数和复制赋值ment运算符,它将抑制其移动特殊成员函数对应项的隐式声明,因此即使在
-std=c++11
模式下,
Foo
也不会被移动,这似乎是答案所暗示的。此外,libc++在c++03和c++11模式下产生相同的输出,因此这是一个QoI问题。正如Lightness所说的s、 要想弄清楚发生了什么,需要深入研究libstdc++实现。据我所知,C++03没有改变
map::operator[]的效果
与C++98相比。谢谢,这很有趣,也很彻底。在C++11中,效果的措辞似乎与LWG 334中的一样。@tahsmith是的,我在那里停得有点早。我现在已经包括了C++11的措辞。挖掘gcc的git历史也很有趣!幸运的是,我记得有一些LWG问题与
操作符[]有关
,因此查找LWG 334并不太困难。作为一个小的后续行动,您是否知道是否对LWG 334中的以下注释做了任何处理?“我们可能还希望在第17条的某个地方有一个笼统的声明,说明我们不打算将示例代码片段的语义解释为具体说明制作了多少副本。”@tahsmith抱歉,我不知道。不过,我在C++14的§17中找不到任何类似的内容。我想他们(目前)已经找到了通过使单个函数的规范更加抽象(就像
map::operator[]
)解决了这个问题。
__i = insert(__i, value_type(__k, _Tp()));