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.
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.