Section: 24.5.2.2.2 [back.insert.iter.ops], 24.5.2.3.2 [front.insert.iter.ops], 24.5.2.4.2 [insert.iter.ops] Status: C++11 Submitter: Daniel Krügler Opened: 2010-03-28 Last modified: 2023-02-07
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.
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); [..] };
back_insert_iterator<Container>& operator=(const typename Container::const_referencevalue_type& value);1 Effects:
container->push_back(value)
;
2 Returns:*this
.
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); [..] };
front_insert_iterator<Container>& operator=(const typename Container::const_referencevalue_type& value);1 Effects:
container->push_front(value)
;
2 Returns:*this
.
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); [..] };
insert_iterator<Container>& operator=(const typename Container::const_referencevalue_type& value);1 Effects:
iter = container->insert(iter, value); ++iter;2 Returns:
*this
.