This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++11 status.

### 1334. Insert iterators are broken for some proxy containers compared to C++03

Section: 23.5.2.2.1 [back.insert.iter.ops], 23.5.2.3.1 [front.insert.iter.ops], 99 [insert.insert.iter.op=] Status: C++11 Submitter: Daniel Krügler Opened: 2010-03-28 Last modified: 2021-06-06

Priority: Not Prioritized

View all other issues in [back.insert.iter.ops].

View all issues with C++11 status.

Discussion:

In C++03 this was valid code:

>     #include <vector>
>     #include <iterator>

>     int main() {
>       typedef std::vector<bool> Cont;
>       Cont c;
>       std::back_insert_iterator<Cont> it = std::back_inserter(c);
>       *it = true;
>     }

In C++0x this code does no longer compile because of an ambiguity error for this `operator=` overload pair:

>     back_insert_iterator<Container>&
>     operator=(typename Container::const_reference value);

>     back_insert_iterator<Container>&
>     operator=(typename Container::value_type&& value);

This is so, because for proxy-containers like `std::vector<bool>` the `const_reference` usually is a non-reference type and in this case it's identical to `Container::value_type`, thus forming the ambiguous overload pair

>     back_insert_iterator<Container>&
>     operator=(bool value);

>     back_insert_iterator<Container>&
>     operator=(bool&& value);

The same problem exists for `std::back_insert_iterator`, `std::front_insert_iterator`, and `std::insert_iterator`.

One possible fix would be to require that `const_reference` of a proxy container must not be the same as the `value_type`, but this would break earlier valid code. The alternative would be to change the first signature to

>     back_insert_iterator<Container>&
>     operator=(const typename Container::const_reference& value);

This would have the effect that this signature always expects an lvalue or rvalue, but it would not create an ambiguity relative to the second form with rvalue-references. [For all non-proxy containers the signature will be the same as before due to reference-collapsing and const folding rules]

[ Post-Rapperswil ]

This problem is not restricted to the unspeakable `vector<bool>`, but is already existing for other proxy containers like gcc's `rope` class. The following code does no longer work ([Bug libstdc++/44963]):

>     #include <iostream>
>     #include <ext/rope>

>     using namespace std;

>     int main()
>     {
>          __gnu_cxx::crope line("test");
>          auto ii(back_inserter(line));

>          *ii++ = 'm'; // #1
>          *ii++ = 'e'; // #2

>          cout << line << endl;
>     }

Both lines marked with #1 and #2 issue now an error because the library has properly implemented the current wording state (Thanks to Paolo Calini for making me aware of this real-life example).

The following P/R is a revision of the orignal P/R and was initially suggested by Howard Hinnant. Paolo verified that the approach works in gcc.

> Moved to Tentatively Ready with revised wording after 6 positive votes on c++std-lib.

[ Adopted at 2010-11 Batavia ]

Proposed resolution:

The wording refers to N3126.

1. Change [back.insert.iterator], class `back_insert_iterator` synopsis as indicated:

   >     template <class Container>
   >     class back_insert_iterator :
   >      public iterator<output_iterator_tag,void,void,void,void> {
   >     protected:
   >      Container* container;
   >     public:
   >      [..]
   >      back_insert_iterator<Container>&
   >        operator=(const typename Container::const_referencevalue_type& value);
   >      back_insert_iterator<Container>&
   >        operator=(typename Container::value_type&& value);
   >      [..]
   >     };

2. Change [back.insert.iter.op=] before p. 1 as indicated:

   >     back_insert_iterator<Container>&
   >        operator=(const typename Container::const_referencevalue_type& value);

   > > 1 Effects: `container->push_back(value)`;
   > > 2 Returns: `*this`.

3. Change [front.insert.iterator], class `front_insert_iterator` synposis as indicated:

   >     template <class Container>
   >     class front_insert_iterator :
   >      public iterator<output_iterator_tag,void,void,void,void> {
   >     protected:
   >      Container* container;
   >     public:
   >      [..]
   >      front_insert_iterator<Container>&
   >        operator=(const typename Container::const_referencevalue_type& value);
   >      front_insert_iterator<Container>&
   >        operator=(typename Container::value_type&& value);
   >      [..]
   >     };

4. Change [front.insert.iter.op=] before p.1 as indicated:

   >     front_insert_iterator<Container>&
   >        operator=(const typename Container::const_referencevalue_type& value);

   > > 1 Effects: `container->push_front(value)`;
   > > 2 Returns: `*this`.

5. Change [insert.iterator], class insert_iterator synopsis as indicated:

   >     template <class Container>
   >        class insert_iterator :
   >          public iterator<output_iterator_tag,void,void,void,void> {
   >        protected:
   >          Container* container;
   >          typename Container::iterator iter;
   >        public:
   >          [..]
   >          insert_iterator<Container>&
   >            operator=(const typename Container::const_referencevalue_type& value);
   >          insert_iterator<Container>&
   >            operator=(typename Container::value_type&& value);
   >          [..]
   >        };

6. Change [insert.iter.op=] before p. 1 as indicated:

   >     insert_iterator<Container>&
   >         operator=(const typename Container::const_referencevalue_type& value);

   > > 1 Effects:

   > > >       iter = container->insert(iter, value);
   > > >       ++iter;

   > > 2 Returns: `*this`.