g++ compiler error on deleted assignment operator of pair<string,string>

Viewed 1393

I think the following reduced C++11 code should be valid.

unordered_map<string,string> test;
auto it = remove_if( test.begin(), test.end(),
    [] (const decltype(test)::value_type &entry) { return true; } );

But it fails to compile with g++ 6.3, complaining about a deleted assignment operator of std::pair, but AFAIK that operator is not deleted.

/usr/include/c++/6/bits/stl_algo.h:868:16: error: use of deleted function ‘std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator=( ...
*__result = _GLIBCXX_MOVE(*__first);

Is this a compiler/glibc bug or is the code really invalid for some reason I fail to see?

3 Answers

Let's look at remove_if documentation:

The type of dereferenced ForwardIt must meet the requirements of MoveAssignable.

That is, “given t, a modifiable lvalue expression of type T and rv, an rvalue expression of type T, the expression t = rv must be valid and [behave as expected]”.

Here, you pass unordered_map<string, string>'s iterator to remove_if. Let's have a look. According to unordered_map documentation,

value_type is defined as std::pair<const Key, T>

So, std::pair<const string, string>.

Let's look at pair's operator=. Most notably:

template< class U1, class U2 > pair& operator=( const pair<U1,U2>& other ); does not participate in overload resolution unless std::is_assignable_v<first_type&, const U1&> and std::is_assignable_v<second_type&, const U2&> are both true.

Here, std::is_assignable_v<const string&, const string&> is not true, therefore the operator is not available. Therefore the pair is not MoveAssignable. Therefore remove_if cannot be used on those iterators.

So that would make your code invalid.

std::pair<const string, string> does have deleted assignment operator(s), because you can't change first.

The key_type of all the map-like containers in the STL are const, because otherwise you could break the lookup of elements.

I had a need to assign to a std::pair<A const, B> that was a copy of something I got by dereferencing a std::map<A,B> iterator. I finally used destructor/constructor logic.

// For the placement allocator
#include <new>
...
typedef std::pair<A const, B> PairType;
PairType &rPairWrite(...some source...);
PairType const &rPairRead(...some source...);
// rPairWrite = rPairRead;
rPairWrite.~PairType();
new (&rPairWrite) PairType (someAValue, someBValue);

Stupid syntax games...

Related