Is this non-copyable map legal c++11? GCC 4.7 and MSVS 2010 allow it. Clang 3.1 does not

596 views Asked by At

I have created a non-copyable map which I cannot get to compile with clang. Since clang is meant to be very standards compliant I was wondering if my code was legal. MSVS 2010 and GCC 4.7 compile this code without warnings or errors.

Full code is attached: the problematic line is the last line of main.

= delete needs removing for MSVS 2010

#include <utility>
#include <iostream>
#include <map>

template<typename Key_t, typename Value_t, typename Compare_t = std::less<Key_t> >
class non_copyable_map : public std::map<Key_t,Value_t,Compare_t>
{
  typedef std::map<Key_t,Value_t,Compare_t> BaseType;

  public:
    non_copyable_map() { } 

    non_copyable_map(non_copyable_map&& t) : BaseType(std::move(t)) {}   

    non_copyable_map& operator = (non_copyable_map&& t)
    {   
      if ( this != &t )
      {   
        std::swap<BaseType>(*this,t);
      }   
      return *this;
    }   

  private:
    non_copyable_map(const non_copyable_map&) = delete;
    non_copyable_map& operator = (const non_copyable_map&) = delete;
};

int main(int argc, char* argv[])
{
  non_copyable_map<int, non_copyable_map<int, int> > nestedMap;
  non_copyable_map<int,int> inner;
  inner[3]=4;
  nestedMap[2] = std::move(inner); // THIS LINE CAUSES CLANG PROBLEMS
}

Error message when using clang++-mp-3.1 -std=c++0x -stdlib=libc++ MapOfMaps.cpp is:

In file included from MapOfMaps.cpp:2:
In file included from /usr/include/c++/v1/iostream:40:
In file included from /usr/include/c++/v1/istream:156:
In file included from /usr/include/c++/v1/ostream:134:
In file included from /usr/include/c++/v1/bitset:118:
/usr/include/c++/v1/__bit_reference:26:26: error: no type named '__storage_type' in
      'std::__1::map<int, int, std::__1::less<int>, std::__1::allocator<std::__1::pair<const
      int, int> > >'
    typedef typename _C::__storage_type    __storage_type;
            ~~~~~~~~~~~~~^~~~~~~~~~~~~~
MapOfMaps.cpp:21:25: note: in instantiation of template class
      'std::__1::__bit_reference<std::__1::map<int, int, std::__1::less<int>,
      std::__1::allocator<std::__1::pair<const int, int> > > >' requested here
                                std::swap<BaseType>(*this,t);
                                                    ^
MapOfMaps.cpp:36:15: note: in instantiation of member function 'non_copyable_map<int, int,
      std::__1::less<int> >::operator=' requested here
        nestedMap[2] = std::move(inner);
                     ^
In file included from MapOfMaps.cpp:2:
In file included from /usr/include/c++/v1/iostream:40:
In file included from /usr/include/c++/v1/istream:156:
In file included from /usr/include/c++/v1/ostream:134:
In file included from /usr/include/c++/v1/bitset:118:
/usr/include/c++/v1/__bit_reference:27:26: error: no type named '__storage_pointer' in
      'std::__1::map<int, int, std::__1::less<int>, std::__1::allocator<std::__1::pair<const
      int, int> > >'
    typedef typename _C::__storage_pointer __storage_pointer;
            ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
/usr/include/c++/v1/__bit_reference:33:25: error: no type named '__self' in
      'std::__1::map<int, int, std::__1::less<int>, std::__1::allocator<std::__1::pair<const
      int, int> > >'
    friend typename _C::__self;
           ~~~~~~~~~~~~~^~~~~~
In file included from MapOfMaps.cpp:3:
In file included from /usr/include/c++/v1/map:338:
/usr/include/c++/v1/__tree:1291:14: error: overload resolution selected deleted operator '='
    __pair3_ = _STD::move(__t.__pair3_);
    ~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/v1/__tree:1308:9: note: in instantiation of member function
      'std::__1::__tree<std::__1::pair<int, int>, std::__1::__map_value_compare<int, int,
      std::__1::less<int>, true>, std::__1::allocator<std::__1::pair<int, int> >
      >::__move_assign' requested here
        __move_assign(__t, true_type());
        ^
/usr/include/c++/v1/__tree:1353:5: note: in instantiation of member function
      'std::__1::__tree<std::__1::pair<int, int>, std::__1::__map_value_compare<int, int,
      std::__1::less<int>, true>, std::__1::allocator<std::__1::pair<int, int> >
      >::__move_assign' requested here
    __move_assign(__t, integral_constant<bool,
    ^
/usr/include/c++/v1/map:736:21: note: in instantiation of member function
      'std::__1::__tree<std::__1::pair<int, int>, std::__1::__map_value_compare<int, int,
      std::__1::less<int>, true>, std::__1::allocator<std::__1::pair<int, int> > >::operator='
      requested here
            __tree_ = _STD::move(__m.__tree_);
                    ^
/usr/include/c++/v1/type_traits:2342:9: note: in instantiation of member function
      'std::__1::map<int, int, std::__1::less<int>, std::__1::allocator<std::__1::pair<const
      int, int> > >::operator=' requested here
    __x = _STD::move(__y);
        ^
MapOfMaps.cpp:21:5: note: in instantiation of function template specialization
      'std::__1::swap<std::__1::map<int, int, std::__1::less<int>,
      std::__1::allocator<std::__1::pair<const int, int> > > >' requested here
                                std::swap<BaseType>(*this,t);
                                ^
MapOfMaps.cpp:36:15: note: in instantiation of member function 'non_copyable_map<int, int,
      std::__1::less<int> >::operator=' requested here
        nestedMap[2] = std::move(inner);
                     ^
/usr/include/c++/v1/memory:1918:7: note: candidate function (the implicit copy assignment
      operator) has been explicitly deleted
class __compressed_pair
      ^
4 errors generated.
1

There are 1 answers

2
Howard Hinnant On BEST ANSWER

This may be a clang or libc++ bug (most likely libc++). I'm not duplicating your symptom with tip-of-trunk tools. The latest libc++ headers are here. This is a bit of a shot in the dark, but try this:

    swap(static_cast<BaseType&>(*this),static_cast<BaseType&>(t));

It looks like you're getting tangled up with this swap in <__bit_reference>:

template <class _Cp, class _Dp>
_LIBCPP_INLINE_VISIBILITY inline
void
swap(__bit_reference<_Cp> __x, __bit_reference<_Dp> __y) _NOEXCEPT

which has nothing to do with maps. It just happens to be in scope.