False copies in C ++ 03 libstdC ++ vs C ++ 11

Consider this code:

#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; } 

Compiled in gcc with -std = C ++ 11 and you will get the result,

 Param Default Assignment 

Remove -std = C ++ 11, then you will get

 Param Default Copy Copy Assignment 

with c ++ 11

without

libC ++ example providing excellent C ++ 03 output

Where do the two extra copies come from?

They are associated with calling the index operator, not assignment. (They remain if you delete the assignment.) It seems to me that they are not needed, even in pre-C ++ 11, as the libC ++ example shows.

It was originally motivated by looking at this question.

+6
source share
1 answer

This is LWG 334 :

The C ++ 03 standard provides the following effects for operator[] ([lib.map.access] p1):

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


libstdC ++ implements the insert used by operator[] (in the case when the key does not exist yet), as shown in C ++ 03 mode:

  __i = insert(__i, value_type(__k, mapped_type())); 

__i is the insertion point, it is calculated as

 iterator __i = lower_bound(__k); 

__k is an operator[] parameter.

Creating a temporary value_type(__k, mapped_type()) calls the first copy (from mapped_type() in the value_type pair). The second copy is the result of insert , which copies the value_type pair to the actual node.

Original version since 1997:

 return (*((insert(value_type(k, T()))).first)).second; 

which almost matches the letter of the standard (which did not even exist then!). The last time it was significantly changed in 1998. Before that, he used:

 __i = insert(__i, value_type(__k, _Tp())); 

A commit message says it was

Update to SGI STL 3.11.


Earlier versions of SGI STL (1995) did indicate map::operator[] in the same way as the C ++ 03 standard:

To display m and the key k , m[k] semantically equivalent to (*((m.insert(make_pair(k, T()))).first)).second .

SGI STL v2.03 (1997) has already switched to using value_type instead of make_pair . And, as gcc commit log suggests, the SGI STL implementation has again changed between versions v3.0 (also 1997) and v3.11 (1998) from insert(value_type(.. into a form that is still present in libstdC ++ using lower_bound and creating only a pair if the key does not exist yet.


So, we can say that libstdC ++ implements the first proposed resolution of LWG 334 ( value_type instead of make_pair ). This is not quite what happened, though, looking at his story. It is just after SGI STL . libC ++ does not strictly comply with C ++ 03 in this regard.


libstdC ++ C ++ 11 version of the same statement uses a custom allocation function. The standard C ++ 11 specification map::operator[] follows the proposed LWG 334 resolution:

Effects: If there is no key equivalent to x on the card, inserts value_type(x, T()) into the card.

(where x is the parameter operator[] )

+9
source

Source: https://habr.com/ru/post/988650/


All Articles